bzoj3862Little Devil I(树链剖分+线段树)

Description

There is an old country and the king fell in love with a devil. The devil always asks the king to do some crazy things. Although the king used to be wise and beloved by his people. Now he is just like a boy in love and can’t refuse any request from the devil. Also, this devil is looking like a very cute Loli.
The devil likes to make thing in chaos. This kingdom’s road system is like simply a tree(connected graph without cycle). A road has a color of black or white. The devil often wants to make some change of this system.
In details, we call a path on the tree from a to b consists of vertices lie on the shortest simple path between a and b. And we say an edge is on the path if both its two endpoints is in the path, and an edge is adjacent to the path if exactly one endpoint of it is in the path.
Sometimes the devil will ask you to reverse every edge’s color on a path or adjacent to a path.
The king’s daughter, WJMZBMR, is also a cute loli, she is surprised by her father’s lolicon-like behavior. As she is concerned about the road-system’s status, sometimes she will ask you to tell there is how many black edge on a path.
Initially, every edges is white.

Input

The first line contains an integer T, denoting the number of the test cases.
For each test case, the first line contains an integer n, which is the size of the tree. The vertices be indexed from 1.
On the next n-1 lines, each line contains two integers a,b, denoting there is an edge between a and b. 
The next line contains an integer Q, denoting the number of the operations.
On the next Q lines, each line contains three integers t,a,b. t=1 means we reverse every edge’s color on path a to b. t=2 means we reverse every edge’s color adjacent to path a to b. t=3 means we query about the number of black edge on path a to b.
T<=5.
n,Q<=10^5.
Please use scanf,printf instead of cin,cout,because of huge input.

Output

For each t=3 operation, output the answer in one line.

Sample Input

1
10
2 1
3 1
4 1
5 1
6 5
7 4
8 3
9 5
10 6

10
2 1 6
1 3 8
3 8 10
2 3 4
2 10 8
2 4 10
1 7 6
2 7 3
2 1 4
2 10 10

Sample Output

3

HINT

reverse color means change from white to black or vice virsa.



题目大意就是维护3个操作:
1.对两点之间的路径上的边进行颜色反转
2.对与两点之间的路径相交(即有且仅有一个点在路径上)的边进行颜色反转
3.询问两点路径上有多少黑色的边
1,3操作都好说,但是2操作比较麻烦,我们可以开两棵线段树,一棵维护点的重儿子是否反色(root_on),一棵维护轻儿子是否反色(root_adj).
在进行操作2时, 对于重链, 修改root_adj上的标记, 但是我们要考虑到途中也有重链或许会与路径相连但又不在此路径上 也就是在端点和向上跳的过程中的轻边旁会有相交的重边,此 时对这些点在root_on上标记.
在统计答时,先把路径上root_on的答案加起来,再特判端点是否被修改.
代码
#include <cstdio>
#include <iostream>
#include <cstring>
#define maxn 100005
#define mem(a,b) memset(a,b,sizeof(a))
using namespace std;
inline int read()
{	char c=getchar();int x=0,y=1;
	while(c<'0'||c>'9'){if(c=='-') y=-1;c=getchar();}
	while(c>='0'&&c<='9') x=x*10+c-'0',c=getchar();
	return x*y;
}
int T,n,q,cnt,POS,num,hea[maxn],top[maxn],dp[maxn],f[maxn],tn[maxn],ssum[maxn],son[maxn];
struct road{int en,nex;}ro[maxn<<1];
inline void add(int x,int y){ro[num].en=y;ro[num].nex=hea[x];hea[x]=num++;}
struct tree
{	tree *lch,*rch;int lc,rc,bsum,rev,len;
	tree(int x=0,int y=0):lc(x),rc(y),lch(NULL),rch(NULL),len(0),bsum(0),rev(0){}
	void* operator new (size_t size);
	inline void mt(tree* now){now->bsum=now->lch->bsum+now->rch->bsum;}
	inline void change(tree* now){now->rev^=1;now->bsum=now->len-now->bsum;}
	inline void dn(tree* now){if(now->rev){change(now->lch);change(now->rch);now->rev^=1;}}
	inline void build(int x,int y,tree*& now)
	{	now=new tree(x,y);now->len=y-x+1;int mid=x+y>>1;
		if(x==y) return;
		build(x,mid,now->lch);build(mid+1,y,now->rch);
	}
	inline void reverse(int x,int y,tree* now)
	{	if(now->lc>=x&&now->rc<=y){change(now);return;}
		dn(now);int mid=now->lc+now->rc>>1;
		if(x<=mid) reverse(x,y,now->lch);
		if(mid<y) reverse(x,y,now->rch);
		mt(now);
	}
	inline int query(int x,int y,tree* now)
	{	if(now->lc>=x&&now->rc<=y) return now->bsum;
		int mid=now->lc+now->rc>>1,tmp=0;dn(now);
		if(x<=mid) tmp+=query(x,y,now->lch);
		if(mid<y) tmp+=query(x,y,now->rch);
		return tmp;
	}
}*root_on,*root_adj,mempool[1<<20];
void* tree::operator new(size_t size){return &mempool[++POS];}
inline void dfs1(int x,int y=0)
{	dp[x]=dp[y]+1;ssum[x]=1;f[x]=y;
	for(int i=hea[x];i!=-1;i=ro[i].nex)
	{	int v=ro[i].en;
		if(v==y) continue;
		dfs1(v,x);ssum[x]+=ssum[v];
		if(!son[x]||ssum[son[x]]<ssum[v]) son[x]=v;
	}
}
inline void dfs2(int x,int y)
{	top[x]=y;tn[x]=++cnt;
	if(!son[x]) return;
	dfs2(son[x],y);
	for(int i=hea[x];i!=-1;i=ro[i].nex)
	{	int v=ro[i].en;
		if(v!=f[x]&&v!=son[x]) dfs2(v,v);
	}
}
inline void paint(int x,int y,int o)
{	int t1=top[x],t2=top[y];
	while(t1!=t2)
	{	if(dp[t1]<dp[t2]) swap(t1,t2),swap(x,y);
		if(o==2)
		{	root_adj->reverse(tn[t1],tn[x],root_adj);//所有与路径相交的点都被标记
			root_on->reverse(tn[t1],tn[t1],root_on);
			if(son[x]) root_on->reverse(tn[son[x]],tn[son[x]],root_on);//端点处的重儿子也被反色
		}
		else root_on->reverse(tn[t1],tn[x],root_on);
		x=f[t1];t1=top[x];
	}
	if(dp[x]>dp[y]) swap(x,y);
	if(o==2)
	{	root_adj->reverse(tn[x],tn[y],root_adj);
		root_on->reverse(tn[x],tn[x],root_on);
		if(son[y]) root_on->reverse(tn[son[y]],tn[son[y]],root_on);
	}
	else if(x!=y) root_on->reverse(tn[x]+1,tn[y],root_on);
}
inline int query(int x,int y)
{	int t1=top[x],t2=top[y],tmp=0;
	while(t1!=t2)
	{	if(dp[t1]<dp[t2]) swap(t1,t2),swap(x,y);
		if(t1!=x) tmp+=root_on->query(tn[t1]+1,tn[x],root_on);
		tmp+=root_on->query(tn[t1],tn[t1],root_on)^root_adj->query(tn[f[t1]],tn[f[t1]],root_adj);//如果链顶的边被反转则+1
		x=f[t1];t1=top[x];
	}
	if(dp[x]>dp[y]) swap(x,y);
	if(x!=y) tmp+=root_on->query(tn[x]+1,tn[y],root_on);//只考虑路径上的黑边,所以不需要再考虑端点了
	return tmp;
}
inline void init()
{	mem(son,0);mem(top,0);mem(ssum,0);mem(f,0);mem(dp,0);mem(tn,0);mem(mempool,0);mem(hea,-1);
	POS=0;cnt=0;num=0;
}
int main()
{	T=read();
	while(T--)
	{	init();
		n=read();int x,y,z;
		for(int i=1;i<n;++i){x=read();y=read();add(x,y);add(y,x);}
		dfs1(1);dfs2(1,1);root_on->build(1,n,root_on);root_adj->build(1,n,root_adj);
		q=read();
		for(int i=1;i<=q;++i)
		{	x=read();y=read();z=read();
			if(x==3) printf("%d\n",query(y,z));
			else paint(y,z,x);
		}
	}
	return 0;
}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值