1. 程式人生 > >2018ACM/ICPC南京網路賽B-The writing on the wall (補題)

2018ACM/ICPC南京網路賽B-The writing on the wall (補題)

題目連結

題意:求n*m的區域中不含有黑色格子的矩形數量。

這題給了2s的時限,m只給了100,暴力複雜度可以是O(n*m*m),接近1e9,能過,大概1300+ms。。。因為高中的時候懶得學組合數學,導致現在對計數的東西一竅不通,連沒有黑色格子的情況居然都想不清楚QAQ

看了上面連結的部落格後,有種恍然大明白的感覺,現在開始講講我理解的做法。

首先,假設沒有黑色格子,暴力計數的話可以先列舉右下角的座標(i,j),然後列舉左下角的列號k,對於每一個不同(i,j,k),新增的矩形個數就是行號i(即左上角可能的取值個數)——左下角和右下角確定的情況下理解這個計數應該還是挺簡單的。列舉完後,得到的值就是總的矩形數。

如果有黑色格子,會對計數產生什麼影響呢?我們需要在列舉左下角的時候,同時更新從右下角到左下角的列裡最接近當前行黑色格子的行號,因為這個黑色格子限制了左上角可能的取值個數。至於怎麼更新,可以列舉行時先維護出每一列最接近當前行的黑色格子的行號。

(PS:除了暴力外還研究了一下同校聚聚的分治程式碼,發現在極限資料下也會退化成O(n*m*m),不過思路還是巧妙哇

程式碼如下:

#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
int up[110];
struct node{
	int x,y;
	friend bool operator<(node a,node b){
		return a.x<b.x;
	}
}b[100005];
int main(){
	int T, cas=0;
	scanf("%d", &T);
	while(T--){
		int n, m, K;
		scanf("%d%d%d", &n, &m, &K);
		memset(up,0,sizeof(up));
		for(int i=0; i<K; i++){
			scanf("%d%d", &b[i].x, &b[i].y);
		}
		ll ans=0;int tt=0;
		for(int i=1; i<=n; i++){
			while(tt<K&&b[tt].x<=i){//維護每一列中最接近當前行的黑色格子的行號 
				up[b[tt].y]=max(up[b[tt].y],b[tt].x),tt++;
			}
			for(int j=1; j<=m; j++){
				int minn=i;
				for(int k=j; k>0; k--){
					if(i-up[k]<minn)
					minn=(i-up[k]);
					ans+=minn;
				}
			}
		}
		printf("Case #%d: %lld\n", ++cas, ans);
	}
	return 0;
}