[bzoj1582][Usaco2009 Hol]Holiday Painting 节日画画_线段树
Holiday Painting 节日画画 bzoj-1582 Usaco-2009 Hol
题目大意:给定两个n*m的01网格图。q次操作,每次将第二个网格图的子矩阵全部变成0或1,问每一次操作后两个网格图有多少个格子不一样。
注释:$1\le n\le 5\cdot 10^4$,$1\le m\le 15$,$1\le q\le 10^4$。
想法:由于网格图的列比较少,很容易想到对每行建立一棵线段树。
然后就是线段树上维护的东西:我们考虑直接维护成对应区间中有多少不一样的格子数。
这个属性显然满足可加性。修改怎么办?
修改的时候就是再处理出第一个网格图的区间0的个数。如果赋成0信息就变成区间长度减去0的个数,如果是1信息就是区间0的个数。
最后,附上丑陋的代码... ...
#include <iostream> #include <cstdio> #include <cstring> #include <algorithm> #define N 50001 #define root 1,1,n #define ls now<<1,l,mid #define rs now<<1|1,mid+1,r int n,m,q,res; int ans[16][N<<2],sum[16][N][2],add[16][N<<2]; char s[N]; inline char nc() {static char *p1,*p2,buf[100000]; return (p1==p2)&&(p2=(p1=buf)+fread(buf,1,100000,stdin),p1==p2)?EOF:*p1++;} int rd() {int x=0; char c=nc(); while(!isdigit(c)) c=nc(); while(isdigit(c)) x=(x<<3)+(x<<1)+(c^48),c=nc(); return x;} void build(int id,int now,int l,int r) { if(l==r) { ans[id][now]=sum[id][l][0]-sum[id][l-1][0]; return; } int mid=(l+r)>>1; build(id,ls); build(id,rs); ans[id][now]=ans[id][now<<1]+ans[id][now<<1|1]; } inline void pushdown(int id,int now,int l,int r) { if(add[id][now]^-1) { int mid=(l+r)>>1; add[id][now<<1]=add[id][now]; add[id][now<<1|1]=add[id][now]; ans[id][now<<1]=sum[id][mid][add[id][now]]-sum[id][l-1][add[id][now]]; ans[id][now<<1|1]=sum[id][r][add[id][now]]-sum[id][mid][add[id][now]]; add[id][now]=-1; } } inline void update(int id,int now,int l,int r,int x,int y,int c) { if(x<=l&&r<=y) { add[id][now]=c; ans[id][now]=sum[id][r][c]-sum[id][l-1][c]; return; } pushdown(id,now,l,r); int mid=(l+r)>>1; if(x<=mid) update(id,ls,x,y,c); if(mid<y) update(id,rs,x,y,c); ans[id][now]=ans[id][now<<1]+ans[id][now<<1|1]; } int main() { int i,j,r1,r2,c1,c2,x; n=rd(),m=rd(),q=rd(); for(i=1;i<=n;i++) { scanf("%s",s+1); for(j=1;j<=m;j++) { sum[j][i][0]=sum[j][i-1][0]+(s[j]=='0'); sum[j][i][1]=sum[j][i-1][1]+(s[j]=='1'); } } for(i=1;i<=m;i++)build(i,root); memset(add,-1,sizeof(add)); while(q--) { r1=rd(),r2=rd(),c1=rd(),c2=rd(); x=rd(); res=0; for(i=c1;i<=c2;i++) update(i,root,r1,r2,x); for(i=1;i<=m;i++)res+=ans[i][1]; printf("%d\n",res); } return 0; }
小结:线段树使用起来是极其灵活的哦。
| 欢迎来原网站坐坐! >原文链接<