2018ACM/ICPC南京網路賽B-The writing on the wall (補題)
阿新 • • 發佈:2018-12-31
題意:求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; }