[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;
}

小结:线段树使用起来是极其灵活的哦。

posted @ 2018-09-19 16:25  JZYshuraK_彧  阅读(194)  评论(0编辑  收藏  举报