【图论前缀和 技巧】任

有趣的二维前缀和

题目描述 = 略

题目大意

一个用01矩阵表示的图中,如果两个1号格子相邻则表示它们相互连通,其中保证连通的1号格子无环存在。每一次询问一个子矩阵中的连通块个数。

对于第 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


 

题目分析

基础暴力做法

本题的部分分给的还是非常良心的。我们可以试一试对于每一次的询问都做一次洪水填充。那么复杂度大概是O(nmq)的。

要注意一点,如果每一次询问时都把vis数组memset,会TLE得只剩20分。我们只要每一次染不同的颜色就好了。

 1 #include
 2 const int maxn = 2003;
 3 const int dx[] = {
     0, 1, 0, -1, 0};
 4 const int dy[] = {
     0, 0, 1, 0, -1};
 5 
 6 int n,m,q,a,b,c,d;
 7 int mp[maxn][maxn];
 8 int vis[maxn][maxn];
 9 
10 inline int getint()
11 {
12     char ch = getchar();
13     for (; !isdigit(ch); ch = getchar());
14     return ch-'0';
15 }
16 inline int read()
17 {
18     int num = 0;
19     char ch = getchar();
20     for (; !isdigit(ch); ch = getchar());
21     for (; isdigit(ch); ch = getchar())
22         num = (num<<1)+(num<<3)+ch-48;
23     return num;
24 }
25 void dfs(int x, int y, int col)
26 {
27     if (x < a || x > c || y < b || y > d || vis[x][y]==col || !mp[x][y]) return;
28     vis[x][y] = col;
29     for (int i=1; i<=4; i++)
30         dfs(x+dx[i], y+dy[i], col);
31 }
32 int main()
33 {
34     n = read(), m = read(), q = read();
35     register int ans,i,j,k;
36     for (i=1; i<=n; i++)
37         for (j=1; j<=m; j++)
38             mp[i][j] = getint();
39     for (i=1; i<=q; i++)
40     {
41         a = read(), b = read(), c = read(), d = read();
42         ans = 0;
43         for (j=a; j<=c; j++)
44             for (k=b; k<=d; k++)
45                 if (vis[j][k]!=i && mp[j][k])
46                     dfs(j, k, i),ans++;
47         printf("%d\n",ans);
48     }
49     return 0;
50 }

期望得分 = 实际得分 = 70分。

要一些结论的做法

这种题型————二维的计数问题————大概算是一种套路吧。我们考虑一下二维的前缀和。

仔细观察一下(虽然这挺不显然的),会发现对于无环图,一个矩形区域内的连通块个数=点数-边数。

诶,那么有了这个性质再二维前缀和就非常方便了。

注意横边和竖边是要分开保存的。

一开始我非常奇怪为什么边不保存在一起。后来发现是因为:相同一个区域内的横边和竖边,在数组中保存的二维区间是不一样的。

 1 #include
 2 const int maxn = 2003;
 3 
 4 int f[maxn][maxn],mp[maxn][maxn];
 5 int egs1[maxn][maxn],egs2[maxn][maxn];
 6 int n,m,q;
 7 
 8 inline int getint()
 9 {
10     char ch = getchar();
11     for (; !isdigit(ch); ch = getchar());
12     return ch-'0';
13 }
14 inline int read()
15 {
16     char ch = getchar();
17     int num = 0;
18     for (; !isdigit(ch); ch = getchar());
19     for (; isdigit(ch); ch = getchar())
20         num = (num<<1)+(num<<3)+ch-48;
21     return num;
22 }
23 int main()
24 {
25     n = read(), m = read(), q = read();
26     for (int i=1; i<=n; i++)
27         for (int j=1; j<=m; j++)
28             mp[i][j] = getint(),f[i][j] = f[i-1][j]+f[i][j-1]-f[i-1][j-1]+mp[i][j];
29     for (int i=1; i<=n; i++)
30         for (int j=1; j<=m; j++)
31         {
32             egs1[i][j] = egs1[i-1][j]+egs1[i][j-1]-egs1[i-1][j-1];
33             egs2[i][j] = egs2[i-1][j]+egs2[i][j-1]-egs2[i-1][j-1];
34             if (!mp[i][j]) continue;
35             if (mp[i][j-1]) egs1[i][j]++;
36             if (mp[i-1][j]) egs2[i][j]++;
37         }
38     for (int i=1; i<=q; i++)
39     {
40         int a = read(), b = read(), c = read(), d = read();
41         int ans = f[c][d]+f[a-1][b-1]-f[a-1][d]-f[c][b-1];
42         ans -= egs1[c][d]+egs1[a-1][b]-egs1[a-1][d]-egs1[c][b];      //这里,可以看到横边和竖边的操作是不同的。
43         ans -= egs2[c][d]+egs2[a][b-1]-egs2[a][d]-egs2[c][b-1];
44         printf("%d\n",ans);
45     }
46     return 0;
47 } 

 

 

 

END

 

转载于:https://www.cnblogs.com/antiquality/p/8997616.html

你可能感兴趣的:(【图论前缀和 技巧】任)