1. 程式人生 > 其它 >[USACO20DEC] Rectangular Pasture S

[USACO20DEC] Rectangular Pasture S

洛谷題面

不需 \(\operatorname{Discretization}\&\operatorname{BIT}\) 並且時間複雜度優秀的一篇題解!

本篇題解是對 這篇題解 的補充。

題目大意

網格圖上有 \(N\) 頭奶牛,第 \(i\) 頭奶牛座標為 \((x_i,y_i)\)

現在用一個大小至少為 \(1\) 的矩形覆蓋若干格子,問被覆蓋的區域包含的奶牛集合有多少種情況,包含空集。(矩形恰好完整覆蓋若干格子)

題目分析

我們沒必要將邊界放在空格上。

我們首先對所有座標按照行從小到大排序。之後所有的牛都按照這個順序來操作。

\(y[i]\) 表示第 \(i\) 頭牛的列,\(l[j]\)

表示第 \(j\) 行的牛與當前第 \(i\) 行的牛之間,有多少頭牛在第 \(j\) 行的牛的左邊,\(r[j]\) 表示第 \(j\) 行的牛與當前第 \(i\) 行的牛之間,有多少頭牛在當前第 \(i\) 行的牛的右邊。

因為我們需要求出包含的奶牛集合有多少種情況 ,所以我們不妨討論第 \(i\) 頭奶牛作為下方邊界,第 \(j\) 頭奶牛作為上方邊界。

這樣子,我們就將題目簡化了。接下來分情況討論:

  1. \(j\) 頭牛在第 \(i\) 頭牛的左邊時:

    • 當第 \(k\) 行的牛在第 \(i\) 行的牛在第 \(j\) 頭牛之間且在第 \(j\) 頭牛左邊,即 \(i\lt k\lt j,y[k]\lt y[j]\)
      時:

    \(k\) 一定可以作為當前框定的左邊界,當前情況的方案數為 \((rnum+1)\times(l[j]+1)\)

    \(rnum\) 表示第 \(i\) 行的牛與當前第 \(j\) 行的牛之間,有多少頭牛在第 \(i\) 行的牛右邊。加 \(1\) 是因為可以選擇邊界。

    • 當第 \(k\) 行的牛在第 \(i\) 行的牛在第 \(j\) 頭牛之間且在第 \(i\) 頭牛右邊,即 \(i\lt k\lt j,y[k]\gt y[i]\) 時:

    \(k\) 一定可以作為當前框定的右邊界,當前情況的方案數為 \((lnum+1)\times(r[j]+1)\)

    \(lnum\) 表示第 \(i\)

    行的牛與當前第 \(j\) 行的牛之間,有多少頭牛在第 \(j\) 行的牛左邊。加 \(1\) 是因為可以選擇邊界。

  2. \(j\) 頭牛在第 \(i\) 頭牛的右邊時同理。


時間複雜度 \(\mathcal{O}(n^2)\)

程式碼

#define int long long

const int ma=2505;

struct Node
{
	int x;
	
	int y;
};

Node node[ma];

int l[ma],r[ma];
//l[j]:排序後,第 j 行的牛與當前第 i 行的牛之間,有多少頭牛在第 j 行的牛的左邊
//r[j]:排序後,第 j 行的牛與當前第 i 行的牛之間,有多少頭牛在當前第 i 行的牛的右邊

int n;

inline bool cmp(Node x,Node y)
{
	if(x.x!=y.x)
	{
		return x.x<y.x;
	}
	
	return x.y<y.y;
}

#undef int

int main(void)
{
	#define int long long
	
	n=read();
	
	for(register int i=1;i<=n;i++)
	{
		node[i].x=read(),node[i].y=read();
	}
	
	sort(node+1,node+n+1,cmp);
	
	int ans=1;
	 
	for(register int i=1;i<=n;i++)
	{
		ans++;
		
		int lnum=0,rnum=0;
		
		for(register int j=i-1;j>=1;j--)
		{
			if(node[i].y>node[j].y)
			{
				ans+=(rnum+1)*(l[j]+1);
				
				lnum++,r[j]++; 
			}
			
			else
			{
				ans+=(lnum+1)*(r[j]+1);
				
				rnum++,l[j]++;
			}
		}
	}
	
	printf("%lld\n",ans);
	
	return 0;
}