RQNOJ 60(zkw线段树-xor区间与区间求和)

查看题目 Show Problem

题目:美丽的中国结

问题编号:60


题目描述

题目背景
kitty刚刚高三毕业.看到同学们都回家的回家,旅游的旅游,她的心里有些落寞.英俊潇洒风流倜傥迷倒万千KL却仅对kitty感冒的fish看在眼里,急在心里.一天,fish提出和kitty两个人一起外出旅游.kitty犹豫了几天,想好能瞒过家长的理由后(要问是什么……自己猜去),答应了.fish很高兴地带着kitty去登记了(别想歪,登记旅游团而已……),日照青岛五日游.
当然啦,他们玩得很高兴.虽然这次旅行是fish先提议的,但kitty因为玩得很畅快(刚高考完嘛),所以想送给fish一份礼物,一份能让见多识广的fish都无法忘怀的礼物.她从路边 9¾站台的某算命先生那里得知中国结具有增加RP的效果,而这正是fish所需要的,因此她决定动手给fish编一个奇特的中国结.

题目描述
中国结形式多样,fish会喜欢什么样的呢?思考几天后,kitty决定给fish编一个树状的中国结.这个中国结有n个结点(编号1,2,…,n),这n个结点之间总共恰有n-1条线相连,其中结点1上是树的根.这是一个奇特的中国结,因此它的编织方式也很奇特.在编织过程中的每一步骤,kitty有时需要将一个结点子树里的所有结点i的状态全部取反(如果原来结点i已经打结,则解开i,否则将结点i打结),有时又需要知道一个结点的子树里有多少已经打结的结点,你能帮助可爱的kitty完成这份礼物吗?

数据规模
对于40% 的数据,1≤n≤10000, 1≤m≤20000
对于100%的数据,1≤n≤100000, 1≤m≤100000

输入格式

输入
第一行有个整数n,表示这个中国结有n个结点
以下n-1行,每行有两个整数u和v(1≤u,v≤n),表示结点u和v间有一条线相连;
再一行有个整数m,表示kitty要进行的步骤数
以下m行,每行可能为:
"C x":表示将结点x的子树中所有结点的状态取反(包括x)

"Q x":表示kitty想知道结点x的子树中有多少已经打结的结点(包括x)

输出格式

对于每个“Q x”输出一行整数,表示结点x的子树中有多少已经打结的结点(包括x)

样例输入

5
1 2
1 3
2 4
2 5
5
Q 1
C 1
Q 1
C 2
Q 1

样例输出

0
5
2

zkw线段树在某个区间上取反(xor 1) ,求某个区间的和

zkw线段树的标记永久化在该题中不适用、

所以可以记一般的Lazy标记--

在处理区间时,把这个区间可能用到的点的标记全部下传

处理完区间后update左、右结点,把区间上传(这样就能消除标记上方结点为0对结果的影响)


#include<cstdio>
#include<cstring>
#include<cstdlib>
#include<iostream>
#include<cmath>
#include<cctype>
#include<functional>
#include<algorithm>
using namespace std;
#define MAXN (100000+10)
#define MAXm (100000+10)
#define NDEBUG
int n,m,M,t[MAXN*10]={0},l[MAXN],r[MAXN];
bool mark[MAXN*10]={false};
char s[50];
int head[MAXN*2]={0},next[MAXN*2],edge[MAXN*2],size=0;
void addedge(int u,int v)
{
	edge[++size]=v;
	next[size]=head[u];
	head[u]=size;
}
int tim=0;
bool b[MAXN]={0};
void dfs(int u)
{
	b[u]=1;l[u]=++tim;
	int p=head[u];
	while (p)
	{
		if (!b[edge[p]]) dfs(edge[p]);
		p=next[p];
	}	
	r[u]=++tim;	
}
void pushdown(int x,int p)
{
	if (!x) return;
	pushdown(x>>1,p<<1);	
	if (mark[x>>1])
	{
		mark[x]^=1;mark[x^1]^=1;mark[x>>1]=0;
		t[x]=p-t[x];t[x^1]=p-t[x^1];
	}
	x>>=1;
	
}
void update(int x)
{
	for (x>>=1;x;x>>=1) t[x]=t[x<<1]+t[(x<<1)^1];
}
int main()
{
	#ifndef NDEBUG
	freopen("RQNOJ60.in","r",stdin);
	#endif
	scanf("%d",&n);
	for (int i=1;i<n;i++) {int u,v;scanf("%d%d",&u,&v);addedge(u,v);addedge(v,u);	}
	dfs(1);
	M=1;while (M-2<r[1]+1) M<<=1;		
	scanf("%d",&m);
	#ifndef NDEBUG
//	cout<<M;
	for (int i=1;i<=n;i++) cout<<i<<':'<<l[i]<<'-'<<r[i]<<' ';
	cout<<'\n';
	#endif
	for (int k=1;k<=m;k++) 
	{
		int x;
		scanf("%s%d",s,&x);
		if (s[0]=='Q')
		{
			int ans=0,p1=l[x],p2=r[x];
			p1--;p2++;p1+=M;p2+=M;
			pushdown(p1,1);pushdown(p2,1);
//			cout<<"add: ";
			#ifndef NDEBUG
			cout<<p1<<' '<<p2<<endl;
			#endif
//			cout<<mark[10];
			while (p1^p2^1)
			{
				if (~p1&1) {/*pushdown(p1+1,1);*/ans+=t[p1+1];/*cout<<p1+1<<':'<<t[p1+1]<<' ';*/}
				if (p2&1)  {/*pushdown(p2-1,1);*/ans+=t[p2-1];/*cout<<p2-1<<':'<<t[p2-1]<<' ';*/}
				p1>>=1;p2>>=1;
			}
			cout<<ans/2<<endl;
		}
		else
		{
			int p=1,p1=l[x],p2=r[x];
			p1--;p2++;p1+=M;p2+=M;
			pushdown(p1,1);pushdown(p2,1);
			while (p1^p2^1)
			{
				if (~p1&1) {mark[p1+1]^=1;t[p1+1]=p-t[p1+1];/*cout<<p1+1<<' ';*/} 
				if (p2&1) {mark[p2-1]^=1;t[p2-1]=p-t[p2-1];/*cout<<p2-1<<' ';*/}
				p1>>=1;p2>>=1;p<<=1;
			}
/*			for (int i=1;i<=M*2;i++) if (mark[i]) cout<<i<<':'<<t[i]<<' ';
			cout<<endl;*/
//			update(p1);

			update(l[x]-1+M);
/*			for (int i=1;i<=M*2;i++) if (mark[i]) cout<<i<<':'<<t[i]<<' ';
			cout<<endl;
*/			update(r[x]+1+M);

		}
		#ifndef NDEBUG
		for (int i=1;i<=n;i++) if (mark[i]) cout<<i<<' ';
		cout<<endl;
		#endif
	
	}
	
	
		
	return 0;
} 



  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值