1 条题解
-
0
考虑欧拉平面图公式:。其中 是连通块个数。
按照如下方式建图,边和点如图所示:

对于每个询问,还需要加上询问的矩形外围的一圈边:

答案即为 。 容易计算。 直接二维前缀和。重点在计算 上。
除去一个红色边所在的大连通块,问题变成了计算完全在红框内部(不包含边界)的连通块数量。
方法 1
搜出所有连通块(注意不是颜色块),记录其上下左右边界,问题转化成离线四维数点,两层 cdq 分治即可。
但是发现这样会 TLE,因为搜连通块时加入了大量无用的孤点,特判掉并加一个二位前缀和即可。 ::::success[Code]
#include <bits/stdc++.h> #define rep(i,a,b) for(int i(a);i<b;++i) #define rept(i,a,b) for(int i(a);i<=b;++i) #define il inline #define x1 vjhgof #define y1 asdfas #define x2 tywtgr #define y2 rtfjkl using namespace std; constexpr int N=1001,M=N*N,INF=1e9; struct Comp{ bool tp; int x1,y1,x2,y2,id; bool mk; }a[M],b[M]; int n,m,q,cnt; int r[N][N],d[N][N],f[N][N],ans[N],bit[N]; char s[N][N]; bool vis[N][N]; il void add(int p,int x){while(p<=m+1) bit[p]+=x,p+=p&-p;} il int ask(int p){int res=0;while(p>0) res+=bit[p],p&=p-1;return res;} il bool cmp1(const Comp &a,const Comp &b){return a.x1==b.x1?a.tp<b.tp:a.x1>b.x1;} il bool cmp2(const Comp &a,const Comp &b){return a.y1==b.y1?a.tp<b.tp:a.y1>b.y1;} il bool cmp3(const Comp &a,const Comp &b){return a.x2==b.x2?a.tp<b.tp:a.x2<b.x2;} void dfs(int i,int j,int id){ vis[i][j]=true; a[id].x1=min(a[id].x1,i),a[id].y1=min(a[id].y1,j); a[id].x2=max(a[id].x2,i),a[id].y2=max(a[id].y2,j); if(r[i][j]&&!vis[i][j+1]) dfs(i,j+1,id); if(d[i][j]&&!vis[i+1][j]) dfs(i+1,j,id); if(j&&r[i][j-1]&&!vis[i][j-1]) dfs(i,j-1,id); if(i&&d[i-1][j]&&!vis[i-1][j]) dfs(i-1,j,id); } void cdq2(int l,int r){ if(l==r) return; int mid=l+r>>1,i=l,j=mid+1,k=l; cdq2(l,mid); cdq2(mid+1,r); while(i<=mid&&j<=r){ if(cmp3(a[i],a[j])){ if(!a[i].mk&&!a[i].tp) add(a[i].y2,1); b[k++]=a[i++]; }else{ if(a[j].mk&&a[j].tp) ans[a[j].id]+=ask(a[j].y2); b[k++]=a[j++]; } } while(j<=r){ if(a[j].mk&&a[j].tp) ans[a[j].id]+=ask(a[j].y2); b[k++]=a[j++]; } rept(i2,l,i-1) if(!a[i2].mk&&!a[i2].tp) add(a[i2].y2,-1); while(i<=mid) b[k++]=a[i++]; rept(i,l,r) a[i]=b[i]; } void cdq1(int l,int r){ if(l==r) return; int mid=l+r>>1,i=l,j=mid+1,k=l; cdq1(l,mid); cdq1(mid+1,r); rept(i,l,mid) a[i].mk=false; rept(i,mid+1,r) a[i].mk=true; while(i<=mid&&j<=r) cmp2(a[i],a[j])?b[k++]=a[i++]:b[k++]=a[j++]; while(i<=mid) b[k++]=a[i++]; while(j<=r) b[k++]=a[j++]; rept(i,l,r) a[i]=b[i]; cdq2(l,r); sort(a+l,a+r+1,cmp2); } signed main(){ scanf("%d%d%d",&n,&m,&q); rept(i,1,n) scanf("%s",s[i]+1); rep(i,1,n) rep(j,0,m) r[i][j]=s[i][j+1]!=s[i+1][j+1]; rep(i,0,n) rep(j,1,m) d[i][j]=s[i+1][j]!=s[i+1][j+1]; rept(i,0,n){ rept(j,0,m){ if(!vis[i][j]){ a[++cnt]={0,INF,INF,-INF,-INF,0,0}; dfs(i,j,cnt); if(a[cnt].x1==a[cnt].x2&&a[cnt].y1==a[cnt].y2) ++f[i][j],--cnt; } } } rept(i,1,n) r[i][0]+=r[i-1][0],d[i][0]+=d[i-1][0],f[i][0]+=f[i-1][0]; rept(j,1,m) r[0][j]+=r[0][j-1],d[0][j]+=d[0][j-1],f[0][j]+=f[0][j-1]; rept(i,1,n){ rept(j,1,m){ r[i][j]+=r[i-1][j]+r[i][j-1]-r[i-1][j-1]; d[i][j]+=d[i-1][j]+d[i][j-1]-d[i-1][j-1]; f[i][j]+=f[i-1][j]+f[i][j-1]-f[i-1][j-1]; } } rept(i,1,q){ int x1,y1,x2,y2; scanf("%d%d%d%d",&x1,&y1,&x2,&y2),--x1,--y1; int V=(x2-x1+1)*(y2-y1+1),E=(x2-x1+y2-y1)<<1; E+=d[x2-1][y2-1]-(!x1?0:d[x1-1][y2-1])-d[x2-1][y1]+(!x1?0:d[x1-1][y1]); E+=r[x2-1][y2-1]-(!y1?0:r[x2-1][y1-1])-r[x1][y2-1]+(!y1?0:r[x1][y1-1]); a[++cnt]={1,x1+1,y1+1,x2-1,y2-1,i,0}; ans[i]=E-V+f[x2-1][y2-1]-f[x1][y2-1]-f[x2-1][y1]+f[x1][y1]+1; } sort(a+1,a+cnt+1,cmp1); rept(i,1,cnt) ++a[i].y2; cdq1(1,cnt); rept(i,1,q) printf("%d\n",ans[i]); return 0; }::::
方法 2
经典技巧:对于每个连通块,选取任意一个节点作为其代表节点。只需要统计落在询问边界内的代表节点数,减去其中不完全在边界内的连通块数。
发现不完全在边界内的连通块必然穿过边界。沿着边界扫一遍即可。 ::::success[Code]
#include <bits/stdc++.h> #define rep(i,a,b) for(int i(a);i<b;++i) #define rept(i,a,b) for(int i(a);i<=b;++i) #define il inline #define x1 vjhgof #define y1 asdfas #define x2 tywtgr #define y2 rtfjkl #define fi first #define se second #define pii pair<int,int> using namespace std; constexpr int N=1001; int n,m,q,cnt; int r[N][N],d[N][N],f[N][N]; pii mk[N][N]; char s[N][N]; bool vis[N][N]; set<pii> st; void dfs(int i,int j,pii id){ vis[i][j]=true; mk[i][j]=id; if(r[i][j]&&!vis[i][j+1]) dfs(i,j+1,id); if(d[i][j]&&!vis[i+1][j]) dfs(i+1,j,id); if(j&&r[i][j-1]&&!vis[i][j-1]) dfs(i,j-1,id); if(i&&d[i-1][j]&&!vis[i-1][j]) dfs(i-1,j,id); } signed main(){ scanf("%d%d%d",&n,&m,&q); rept(i,1,n) scanf("%s",s[i]+1); rep(i,1,n) rep(j,0,m) r[i][j]=s[i][j+1]!=s[i+1][j+1]; rep(i,0,n) rep(j,1,m) d[i][j]=s[i+1][j]!=s[i+1][j+1]; rept(i,0,n){ rept(j,0,m){ if(!vis[i][j]){ ++f[i][j]; dfs(i,j,{i,j}); } } } rept(i,1,n) r[i][0]+=r[i-1][0],d[i][0]+=d[i-1][0],f[i][0]+=f[i-1][0]; rept(j,1,m) r[0][j]+=r[0][j-1],d[0][j]+=d[0][j-1],f[0][j]+=f[0][j-1]; rept(i,1,n){ rept(j,1,m){ r[i][j]+=r[i-1][j]+r[i][j-1]-r[i-1][j-1]; d[i][j]+=d[i-1][j]+d[i][j-1]-d[i-1][j-1]; f[i][j]+=f[i-1][j]+f[i][j-1]-f[i-1][j-1]; } } rept(i,1,q){ int x1,y1,x2,y2; scanf("%d%d%d%d",&x1,&y1,&x2,&y2),--x1,--y1; int V=(x2-x1+1)*(y2-y1+1),E=(x2-x1+y2-y1)<<1,K=1; st.clear(); E+=d[x2-1][y2-1]-(!x1?0:d[x1-1][y2-1])-d[x2-1][y1]+(!x1?0:d[x1-1][y1]); E+=r[x2-1][y2-1]-(!y1?0:r[x2-1][y1-1])-r[x1][y2-1]+(!y1?0:r[x1][y1-1]); K+=f[x2-1][y2-1]-f[x1][y2-1]-f[x2-1][y1]+f[x1][y1]; auto check=[x1,y1,x2,y2](pii p)->bool{ if(p.fi<=x1||p.fi>=x2||p.se<=y1||p.se>=y2) return false; if(st.count(p)) return false; return st.emplace(p),true; }; rept(i,x1,x2){ K-=check(mk[i][y1]); K-=check(mk[i][y2]); } rept(j,y1+1,y2-1){ K-=check(mk[x1][j]); K-=check(mk[x2][j]); } printf("%d\n",E-V+K); } return 0; }::::
- 1
信息
- ID
- 7062
- 时间
- 3000ms
- 内存
- 512MiB
- 难度
- 10
- 标签
- 递交数
- 12
- 已通过
- 2
- 上传者