1. 程式人生 > >liu_runda 給辣雞蒟蒻做的 NOIP模擬賽 1.0 第二題 任(duty) 題解

liu_runda 給辣雞蒟蒻做的 NOIP模擬賽 1.0 第二題 任(duty) 題解

cfa 由於 幹什麽 truct 失去 格子 ace ide ++

問題 B: 任(duty)

時間限制: 2 Sec 內存限制: 512 MB

題目描述

liu_runda退役之後就失去夢想開始鹹魚生活了…

Bilibili夏日畫板活動中,所有人都可以在一塊畫板上進行像素畫創作.UOJ群有一群無聊的人決定在畫板上創作一個50*50的UOJ的LOGO.如下圖.

這塊畫板實際上是很大的矩形網格.一個網格是一像素.

一個人每三分鐘才能畫一個像素.所以liu_runda的鹹魚生活非常無聊.

郭神表示他實在是看不下去liu_rudna這只頹狗了,於是隨手出了一道神題,liu_runda不會做,於是給出到聯考裏了.

在畫板上有一片黑白相間的矩形區域滿足這樣的性質:如果認為相同顏色的方塊可以在上下左右四個方向連通,那麽任意兩個黑色方塊要麽不連通,要麽連通但之間只有一條簡單路徑(不重復經過同一個格子的路徑).

這個矩形區域有N行M列,從上到下依次為第1,2,3…N-1,N行,從左到右依次為第1,2,3…M-1,M列.

每次郭神會詢問這片矩形區域內的一個子矩形.在只考慮這個子矩形內的像素時(即從子矩形內部不能和子矩形之外的像素相連通),問這個子矩形內的黑色方塊組成了多少連通塊.

如果不能完成這個任務,liu_runda就會被郭神批判一番…

【輸入格式】

第一行三個整數N,M,Q,表示矩形區域有N行M列,有Q組詢問.

接下來N行,每行一個長為M的01字符串.0表示白色,1表示黑色.第i行第j個字符表示第i行j列的顏色,

接下來Q行,每行4個整數x1,y1,x2,y2,(x1<=x2,y1<=y2)表示選出的矩形區域的兩個對角.即選出一個左上角為第x1行第y1列,右下角為第x2行第y2列,包含x2-x1+1行,y2-y1+1列的區域.

【輸出格式】

Q行,第i行一個整數ans表示第i組詢問的答案.

【樣例輸入1】

3 4 4

1101

0110

1101

1 1 3 4

1 1 3 1

2 2 3 4

1 2 2 4

【樣例輸出1】

3

2

2

2

【樣例輸入2】

5 5 6

11010

01110

10101

11101

01010

1 1 5 5

1 2 4 5

2 3 3 4

3 3 3 3

3 1 3 5

1 1 3 4

【樣例輸出2】

3

2

1

1

3

2

【數據範圍】

對於第1,2個測試點,Q=1

對於第3,4個測試點,N=1

對於第5,6,7個測試點,N=2

對於第8個測試點,N,M<=1000

對於第9個測試點,N,M<=1500

對於全部測試點,1<=N,M<=2000,1<=Q<=200000,1<=x1<=x2<=N,1<=y1<=y2<=M,保證任意兩個黑色像素之間最多只有一條簡單路徑.

  

  這道題語文負分的我讀了三遍題,手推了兩個樣例才明白到底要幹什麽,耽誤了10分鐘。

  做完第一題就發現這次和之前完全不一樣,是標準的NOIP模擬題,然後現在心中默默盤算了一下,這道題應該是需要拿60++,AC最好。然後就開始考慮對策。

  看到數據範圍之後很明顯,我們必須要搞到最基本的70分,在加上第三題暴力20分,190分就可以保底了。

  首先第1,2個點,很明顯,暴力bfs打一會就拿到20分了。然後是N=1這兩個點,當時想到的是前綴和思想,畢竟就一條格子,我們只要維護一個前綴和記錄該點即其之前的格子有多少個聯通塊,如果x1,y1為黑塊就把兩個前綴和的差+1就是答案了,否則直接輸出前綴和的差。然後打完N=1之後就開始去想N=2,想了一會發現對於不跨行的和之前同理,跨行的只要處理方式變化一下其他和之前沒有任何變化就好了。就這樣70分Get。

技術分享
  1 #include<iostream>
  2 #include<cstdlib>
  3 #include<cstdio>
  4 #include<cstring>
  5 #include<queue>
  6 #include<algorithm>
  7 #include<cmath>
  8 #include<map>
  9 #define N 2005
 10 using namespace std;
 11 int a[N][N],n,m,q,x11,x2,y11,y2,zy[10][5],sum[N][N];
 12 bool fw[N][N];
 13 char b[N];
 14 bool fro[N][N][5];
 15 bool check(int x,int y)
 16 {
 17     if(!a[x][y]||x<x11||x>x2||y<y11||y>y2)return 0;
 18     return 1;
 19 }
 20 struct inf
 21 {
 22     int x,y;
 23 };
 24 void bfs(int xx,int yy)
 25 {
 26     queue<inf> q1;
 27     inf aa;
 28     aa.x=xx,aa.y=yy;
 29     q1.push(aa);
 30     fw[xx][yy]=1;
 31     while(!q1.empty())
 32     {
 33         inf tt=q1.front();q1.pop();
 34         int x=tt.x,y=tt.y;
 35         for(int i=1;i<=4;i++)
 36         {
 37             int tx=x+zy[i][1],ty=y+zy[i][2];
 38             if(!check(tx,ty))continue;
 39             if(fw[tx][ty])
 40             {
 41                 if(i==1)fro[tx][ty][3]=1;
 42                 if(i==2)fro[tx][ty][4]=1;
 43                 if(i==3)fro[tx][ty][1]=1;
 44                 if(i==4)fro[tx][ty][2]=1;
 45                 continue;
 46             }
 47             fw[tx][ty]=1;
 48             inf bb;
 49             bb.x=tx,bb.y=ty;
 50             q1.push(bb);
 51         }
 52     }
 53 }
 54 void work1()
 55 {
 56     zy[1][1]=-1,zy[2][2]=-1,zy[3][1]=1,zy[4][2]=1;
 57     for(int i=1;i<=q;i++)
 58     {
 59         int ans=0;
 60         memset(fw,0,sizeof(fw));
 61         scanf("%d%d%d%d",&x11,&y11,&x2,&y2);
 62         for(int j=x11;j<=x2;j++)
 63         {
 64             for(int k=y11;k<=y2;k++)
 65             {
 66                 if(!a[j][k]||fw[j][k])continue;
 67                 ans++;
 68                 bfs(j,k);
 69             }
 70         }
 71         printf("%d\n",ans);
 72     }   
 73 }
 74 void work2()
 75 {
 76     int sum[N];
 77     memset(sum,0,sizeof(sum));
 78     for(int i=1;i<=m;i++)
 79     {
 80         sum[i]=sum[i-1];
 81         if(a[1][i]&&!a[1][i-1])sum[i]++;
 82     }
 83     for(int i=1;i<=q;i++)
 84     {
 85         scanf("%d%d%d%d",&x11,&y11,&x2,&y2);
 86         int ans=sum[y2]-sum[y11]+a[x11][y11];
 87         printf("%d\n",ans);
 88     }
 89 }
 90 void work3()
 91 {
 92     int sum[N][3];
 93     memset(sum,0,sizeof(sum));
 94     for(int i=1;i<=2;i++)
 95     {
 96         for(int j=1;j<=m;j++)
 97         {
 98             sum[j][i]=sum[j-1][i];
 99             if(a[i][j]&&!a[i][j-1])sum[j][i]++;
100         }
101     }
102     for(int i=1;i<=m;i++)
103     {
104         sum[i][0]=sum[i-1][0];
105         if(a[1][i]||a[2][i])
106         {
107             if(a[1][i]&&a[2][i])
108             {
109                 if((!a[1][i-1])&&(!a[2][i-1]))
110                     sum[i][0]++;
111             }
112             else if(a[1][i]&&(!a[2][i]))
113             {
114                 if(!a[1][i-1])
115                     sum[i][0]++;
116             }
117             else
118             {
119                 if(!a[2][i-1])
120                     sum[i][0]++;
121             }
122         }
123     }
124     for(int i=1;i<=q;i++)
125     {
126         scanf("%d%d%d%d",&x11,&y11,&x2,&y2);
127         if(x11==x2)
128             printf("%d\n",sum[y2][x2]-sum[y11][x11]+a[x11][y11]);
129         else
130         {
131             int ju;
132             if(a[x11][y11]||a[2][y11])ju=1;
133             else ju=0;
134             printf("%d\n",sum[y2][0]-sum[y11][0]+ju);
135         }
136     }
137 }
138 int main()
139 {
140     scanf("%d%d%d",&n,&m,&q);
141     for(int i=1;i<=n;i++)
142     {
143         scanf("%s",b+1);
144         for(int j=1;j<=m;j++)
145         {
146             a[i][j]=b[j]-0;
147         }
148     }
149     if(q==1)
150     {
151         work1();
152     }
153     else if(n==1)
154     {
155         work2();
156     }
157     else if(n==2)
158     {
159         work3();
160     }
161     else
162     {
163         for(int i=1;i<=n;i++)
164         {
165             for(int j=1;j<=n;j++)
166             {
167                 if(!fw[i][j]&&a[i][j])
168                 {
169                     bfs(i,j);
170                 }
171             }
172         }
173         for(int i=1;i<=n;i++)
174         {
175             int f=0;
176             bool la=1;
177             for(int j=1;j<=m;j++)
178             {
179                 sum[i][j]=sum[i-1][j];
180                 if(a[i][j])
181                 {
182                     if(!a[i][j-1]&&!a[i-1][j]&&la)f++,la=1;
183                     else if(a[i-1][j]&&a[i][j-1]&&la)f--,la=0;
184                 }
185                 else la=1;
186                 sum[i][j]+=f;
187             }
188         }
189             for(int i=1;i<=q;i++)
190             {
191                 scanf("%d%d%d%d",&x11,&y11,&x2,&y2);
192                 int ans=sum[x2][y2]+sum[x11-1][y11-1]-sum[x11-1][y2]-sum[x2][y11-1];
193                 int la=0;
194                 for(int j=x2;j>x11;j--)
195                 {
196                     if(a[j][y11]&&!fro[j][y11][4]&&fro[j][y11][1]&&!la)ans++,la=1;
197                     else la=0;
198                 }
199                  
200                 if(a[x11][y11])
201                 {
202                     if(!la&&(fro[x11][y11][1]||fro[x11][y11][2]))ans++,la=1;
203                     else la=0;
204                 }else la=0;
205                 for(int j=y11+1;j<y2;j++)
206                 {
207                     if(a[x11][j]&&!fro[x11][j][1]&&fro[x11][j][2]&&!la)ans++,la=1;
208                     else la=0;
209                 }
210                 if(a[x11][y2])
211                 {
212                     if(!fro[x11][y2][1]&&!fro[x11][y2][4]&&fro[x11][y2][2]&&!la)ans++;
213                 }
214                 printf("%d\n",ans);
215             }
216     }
217     return 0;
218 }
70分打法(請自動忽略後半部分)

  然後先去打第三題暴力去了,大概過了30分鐘吧(當然,這段時間裏還想了一會第三題正解),又回來做這道題。

  回來之後開始聯想NOIP試題套路,然後就想到了位於同樣位置的“天天愛跑步”,那道題是通過部分分打法啟發選手打出正解,那麽這道題是不是呢?當時其實想岔了,因為我之前維護的前綴和就是聯通塊個數,於是乎我就傻乎乎的維護了一個至今還不知道對不對的求聯通塊的個數的二維數組,然後對於單個詢問還是O(m+n)的復雜度出理,連可以O(n*m)預處理都忘了。果斷T掉,結果一開始還在撤銷的時候少打一下,險些編譯錯誤,還好之後又發現了,有驚無險……要是聯賽犯這種錯誤我肯定想死的心都有了。

  其實,當時對於正解思路還有另一個想法,就是二維樹狀數組,因為如果O(q*log(n*m))貌似也是可行的。只不過由於N=1,N=2部分分中沒用到所以否決了這個想法。

  關於考試說完了,下面說正解。


  其實當時我推理推對了一半,的確和部分分的前綴和有關,不過我們A掉這道題首先得先想到一件事,對於一個無環圖中,聯通塊個數等於點數-邊數,這也就是為什麽輸入數據要保證這個奇怪的簡單路徑(當時我註意到了這點,但是也沒有去想太多,只是顧著去想正解了)。所以我們只需要維護四個前綴和,點數,邊數,橫向連邊數,縱向連邊數然後O(1)輸出。 技術分享
 1 #include<iostream>
 2 #include<cstdlib>
 3 #include<cstdio>
 4 #include<cstring>
 5 #include<queue>
 6 #include<algorithm>
 7 #include<cmath>
 8 #include<map>
 9 #define N 2005
10 using namespace std;
11 int a[N][N],n,m,q,x11,x2,y11,y2,sum[N][N],sum2[N][N],sum3[N][N],sum4[N][N];
12 char b[N];
13 int main()
14 {
15     scanf("%d%d%d",&n,&m,&q);
16     for(int i=1;i<=n;i++)
17     {
18         scanf("%s",b+1);
19         for(int j=1;j<=m;j++)
20         {
21             a[i][j]=b[j]-0;
22         }
23     }
24     for(int i=1;i<=n;i++)
25     {
26         for(int j=1;j<=m;j++)
27         {
28             sum[i][j]=sum[i-1][j]+sum[i][j-1]-sum[i-1][j-1]+a[i][j];
29         }
30     }
31     for(int i=1;i<=n;i++)
32     {
33         int f=0,t=0;
34         for(int j=1;j<=m;j++)
35         {
36             if(a[i][j]&&a[i][j-1]) f++;
37             if(a[i][j]&&a[i-1][j]) f++,t++;
38             sum2[i][j]=sum2[i-1][j]+f;
39             sum3[i][j]=t;
40         }
41     }
42     for(int i=1;i<=m;i++)
43     {
44         int f=0;
45         for(int j=1;j<=n;j++)
46         {
47             if(a[j][i]&&a[j][i-1]) f++;
48             sum4[j][i]=f;
49         }
50     }
51     for(int i=1;i<=q;i++)
52     {
53         scanf("%d%d%d%d",&x11,&y11,&x2,&y2);
54         printf("%d\n",sum[x2][y2]-sum[x11-1][y2]-sum[x2][y11-1]+sum[x11-1][y11-1]-(sum2[x2][y2]-sum2[x11-1][y2]-sum2[x2][y11-1]+sum2[x11-1][y11-1]-(sum3[x11][y2]-sum3[x11][y11-1])-(sum4[x2][y11]-sum4[x11-1][y11])));
55     }
56     return 0;
57 }
真.AC代碼   對於這道題,遺憾說實在的,並不是太大,畢竟我不記得這個結論了,但如果我記得這個結論我可以保證我可以A掉,但沒有如果……

liu_runda 給辣雞蒟蒻做的 NOIP模擬賽 1.0 第二題 任(duty) 題解