【bzoj2243】[SDOI2011]染色(树链剖分)

题目:

我是超链接

题解:

这个题要维护左右端点的颜色以及中间的颜色段数

然后能说的只有各个数组的意义了

num[i]真实树中的点i在线段树最底层的编号;tree[i]线段树底层编号为i的点在真树中的编号;woc[i]底层编号为i的点在整棵线段树中的编号

最后要注意的就是数据范围了,deep啥的都是N,线段树中是4N,连边的是2N

代码:
#include <cstdio>
#include <iostream> 
#define N 200000
#define MIN -1e9
using namespace std;
int nxt[N*2],point[N*2],v[N*2],tot=0,cnt=0,n;
int deep[N],size[N],fa[N],top[N],son[N],tree[N],num[N],totw=0;
int sum[N*4],ll[N*4],rr[N*4],delta[N*4],w[N],woc[N*4+5];
void addline(int x,int y)
{
	++tot; nxt[tot]=point[x]; point[x]=tot; v[tot]=y;
	++tot; nxt[tot]=point[y]; point[y]=tot; v[tot]=x;
} 
void updata(int now)
{
	sum[now]=sum[now<<1]+sum[now<<1|1];
	if (rr[now<<1]==ll[now<<1|1]) sum[now]--;
	ll[now]=ll[now<<1];
	rr[now]=rr[now<<1|1];
}
void pushdown(int now)
{ 
	if (delta[now])
	{
		delta[now<<1]=delta[now];
		delta[now<<1|1]=delta[now];
		sum[now<<1]=sum[now<<1|1]=1;
		ll[now<<1]=rr[now<<1]=delta[now];
		ll[now<<1|1]=rr[now<<1|1]=delta[now];
		delta[now]=0;
	}
}
void dfs_1(int now,int dep,int faa)
{
	deep[now]=dep;
	size[now]=1;
	fa[now]=faa;
	int maxx=MIN;
	for (int i=point[now];i;i=nxt[i])
	  if (v[i]!=faa)
	  {
	  	dfs_1(v[i],dep+1,now);
	  	size[now]+=size[v[i]];
	  	if (size[v[i]]>maxx)
	  	{
	  		maxx=size[v[i]];
	  		son[now]=v[i];
		}
	  }
}
void dfs_2(int now,int faa)
{
	if (son[faa]!=now) top[now]=now;
	else top[now]=top[faa];
	num[now]=++totw;
	if (son[now])
	{
		dfs_2(son[now],now);
		for (int i=point[now];i;i=nxt[i])
		  if (v[i]!=son[now] && v[i]!=faa)
		    dfs_2(v[i],now);
	}
}
void build(int now,int l,int r)
{
	if (l==r)
	{
		woc[l]=now;
		sum[now]=1;
		ll[now]=rr[now]=w[tree[l]];
		return;
	}
	int mid=(l+r)>>1;
	build(now<<1,l,mid);
	build(now<<1|1,mid+1,r);
	updata(now);
}                                                                                                                          
void change(int now,int l,int r,int lrange,int rrange,int v)
{
	if (l>=lrange && r<=rrange)
	{
		sum[now]=1;
		ll[now]=rr[now]=v;
		delta[now]=v;
		return;
	}
	pushdown(now);
	int mid=(l+r)>>1;
	if (mid>=lrange) change(now<<1,l,mid,lrange,rrange,v);
	if (mid<rrange) change(now<<1|1,mid+1,r,lrange,rrange,v);
	updata(now);
}
int qurry(int now,int l,int r,int lrange,int rrange)
{
	if (l>=lrange && r<=rrange) return sum[now];
	pushdown(now);
	int mid=(l+r)>>1,ans=0;bool qd1,qd2;qd1=false; qd2=false;
	if (mid>=lrange){ans+=qurry(now<<1,l,mid,lrange,rrange);qd1=true;}
	if (mid<rrange){ans+=qurry(now<<1|1,mid+1,r,lrange,rrange);qd2=true;}
	if (qd1 && qd2 && ll[now<<1|1]==rr[now<<1]) ans--;
	updata(now);
	return ans;
}
void work(int u,int v,int c,int id)
{
	int f1=top[u],f2=top[v],summ=0;
	while (f1!=f2) 
	{
		if (deep[f1]<deep[f2]) 
		{
			swap(f1,f2); swap(u,v);
		}
		if (id==1) change(1,1,n,num[f1],num[u],c); 
		else{
			summ+=qurry(1,1,n,num[f1],num[u]);
			int zz=qurry(1,1,n,num[fa[f1]],num[fa[f1]]);
			zz=qurry(1,1,n,num[f1],num[f1]);
			if (ll[woc[num[f1]]]==ll[woc[num[fa[f1]]]]) summ--;
		}
		u=fa[f1]; f1=top[u];
	}
	if (num[u]>num[v]) swap(u,v);
	if (id==1) change(1,1,n,num[u],num[v],c);
	else{
		summ+=qurry(1,1,n,num[u],num[v]);
		printf("%d\n",summ);
	}
}
int main()
{
	int m,i,x,y,u,c,vv;
	scanf("%d%d",&n,&m);
	for (i=1;i<=n;i++)
	  scanf("%d",&w[i]);
	for (i=1;i<=n-1;i++)
	{
		scanf("%d%d",&x,&y);
		addline(x,y);
	}
	dfs_1(1,1,0);
	dfs_2(1,0);
	for (i=1;i<=n;i++) tree[num[i]]=i;
	build(1,1,n);
	for (i=1;i<=m;i++)
	{
		char st[5];
		scanf("%s",st);
		if (st[0]=='C')
		{
			scanf("%d%d%d",&u,&vv,&c);
			work(u,vv,c,1);
		} 
		else
		{
			scanf("%d%d",&u,&vv);
			work(u,vv,0,2);
		}
	}  
}



评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值