luogu2420 让我们异或和吧

12 篇文章 0 订阅
11 篇文章 0 订阅

题目

  https://www.luogu.org/problem/show?pid=2420

题解

  小知识:异或xor 满足结合律、交换律。另外A xor A=0,也就是说A xor B xor C=E,那么B xor C = E xor A,也就是说可以前缀和。

  做法一:树链剖分,线段树维护。复杂度O(Nlog^2N)(注意坑点:因为权值在边上,所以交界处的值不能算进去)

  做法二:树上前缀和,用s[x]表示根节点到x的路径上的权值异或值,那么两个点上路径的异或值就是s[a] xor s[b]。复杂度O(N+M)

  做法三:LCT,把边化成点,然后瞎搞。代码老是TLE....我也没办法啊,毕竟常数大,暂且先这样吧

代码

//树链剖分 
#include <cstdio>
#include <algorithm>
#define maxn 200010
using namespace std;
int N, M, head[maxn], next[maxn], to[maxn], tmp[maxn], val[maxn], w[maxn], fa[maxn],
	size[maxn], son[maxn], tot, tim, deep[maxn], tid[maxn], top[maxn];
struct segtree
{
	int l, r, w;
	segtree *lch, *rch;
	segtree(){w=0;lch=rch=0;}
}*root;
void adde(int a, int b, int v){to[++tot]=b;w[tot]=v;next[tot]=head[a];head[a]=tot;}
void build(segtree *p, int l, int r)
{
	int mid=(l+r)>>1;
	p->l=l,p->r=r;
	if(l==r){p->w=val[l];return;}
	build(p->lch=new segtree,l,mid);
	build(p->rch=new segtree,mid+1,r);
	p->w=p->lch->w xor p->rch->w;
}
int segsum(segtree *p, int l, int r)
{
	int mid=(p->l+p->r)>>1, ans=0;
	if(l<=p->l and r>=p->r)return p->w;
	if(l<=mid)ans=ans xor segsum(p->lch,l,r);
	if(r>mid)ans=ans xor segsum(p->rch,l,r);
	return ans;
}
void dfs1(int pos)
{
	int p, x;
	size[pos]=1;
	for(p=head[pos];p;p=next[p])
	{
		x=to[p];
		if(x==fa[pos])continue;
		fa[x]=pos;
		deep[x]=deep[pos]+1;
		tmp[x]=w[p];
		dfs1(x);
		if(son[pos]==0 or size[x]>size[son[pos]])son[pos]=x;
		size[pos]+=size[x];
	}
}
void dfs2(int pos, int tp)
{
	int p, x;
	tid[pos]=++tim;
	top[pos]=tp;
	if(son[pos])dfs2(son[pos],tp);
	for(p=head[pos];p;p=next[p])
	{
		x=to[p];
		if(x==fa[pos] or x==son[pos])continue;
		dfs2(x,x);
	}
}
int sum(int a, int b)
{
	int ta=top[a], tb=top[b], ans=0;
	while(ta!=tb)
	{
		if(deep[ta]<deep[tb])swap(ta,tb),swap(a,b);
		ans=ans xor segsum(root,tid[ta],tid[a]);
		a=fa[ta];ta=top[a];
	}
	if(deep[a]>deep[b])swap(a,b);
	ans=ans xor segsum(root,tid[a],tid[b]);
	ans=ans xor segsum(root,tid[a],tid[a]);
	return ans;
}
int main()
{
	int i, a, b, c, M;
	scanf("%d",&N);
	for(i=1;i<N;i++)scanf("%d%d%d",&a,&b,&c),adde(a,b,c),adde(b,a,c);
	dfs1(1);
	dfs2(1,1);
	for(i=1;i<=N;i++)val[tid[i]]=tmp[i];
	build(root=new segtree,1,tim);
	scanf("%d",&M);
	for(i=1;i<=M;i++)
	{
		scanf("%d%d",&a,&b);
		printf("%d\n",sum(a,b));
	}
	return 0;
}
//树上前缀和
#include <cstdio>
#include <algorithm>
#define maxn 200010
using namespace std;
int N, M, head[maxn], to[maxn], w[maxn], next[maxn], tot, s[maxn];
void adde(int a, int b, int c){to[++tot]=b;w[tot]=c;next[tot]=head[a];head[a]=tot;}
void input()
{
	int i, a, b, c;
	scanf("%d",&N);
	for(i=1;i<N;i++)scanf("%d%d%d",&a,&b,&c),adde(a,b,c),adde(b,a,c);
}
void dfs(int pos, int pre)
{
	int p;
	for(p=head[pos];p;p=next[p])
	{
		if(to[p]==pre)continue;
		s[to[p]]=s[pos] xor w[p];
		dfs(to[p],pos);
	}
}
int main()
{
	int i, a, b, m;
	input();
	dfs(1,0);
	scanf("%d",&m);
	for(i=1;i<=m;i++)
	{
		scanf("%d%d",&a,&b);
		printf("%d\n",s[a] xor s[b]);
	}
	return 0;
}
//LCT
#include <cstdio>
#include <algorithm>
#define maxn 500000
using namespace std;
int N, M;
struct node
{
	int sum, rev, w;
	node *f, *ch[2];
}nd[maxn], *s[maxn];
inline int read(int x=0)
{
	char c=getchar();
	while(c<48 or c>57)c=getchar();
	while(c>=48 and c<=57)x=x*10+c-48,c=getchar();
	return x;
}
inline int getwh(node *x)
{if(!x->f)return -1;if(x->f->ch[0]==x)return 0;if(x->f->ch[1]==x)return 1;return -1;}
inline bool isroot(node *x){return getwh(x)==-1;}
inline void join(node *x, node *y, int wh){if(x)x->f=y;if(y)y->ch[wh]=x;}
inline void rever(node *x){if(x)x->rev^=1;}
inline void pushdown(node *x)
{
	if(x->rev)
	{
		swap(x->ch[0],x->ch[1]);
		rever(x->ch[0]),rever(x->ch[1]);
		x->rev=0;
	}
}
inline void pushup(node *x)
{
	x->sum=x->w;
	if(x->ch[0])x->sum^=x->ch[0]->sum;
	if(x->ch[1])x->sum^=x->ch[1]->sum;
}
inline void rotate(node *x)
{
	node *y=x->f, *z=y->f;
	int c=getwh(x);
	if(isroot(y))x->f=y->f;else join(x,z,getwh(y));
	join(x->ch[!c],y,c);
	join(y,x,!c);
	pushup(y),pushup(x);
}
inline void splay(node *x)
{
	node *y; int top=0;
	for(y=x;!isroot(y);y=y->f)s[++top]=y;s[++top]=y;
	for(;top;top--)pushdown(s[top]);
	while(!isroot(x))
	{
		y=x->f;
		if(isroot(y)){rotate(x);return;}
		if(getwh(x)^getwh(y))rotate(x);else rotate(y);
		rotate(x);
	}
}
inline void access(node *x)
{
	node *t=0;
	while(x)
	{
		splay(x);
		x->ch[1]=t;
		pushup(x);
		t=x;x=x->f;
	}
}
inline void makeroot(node *x){access(x);splay(x);rever(x);}
inline void link(node *x, node *y){makeroot(x);x->f=y;}
void init()
{
	int a, b, c, i;
	N=read();
	for(i=1;i<N;i++)
	{
		a=read(),b=read(),c=read();
		nd[N+i].w=nd[N+i].sum=c;
		link(nd+N+i,nd+a);link(nd+N+i,nd+b);
		access(nd+a),splay(nd+a);
	}
}
inline int work(int a, int b)
{
	makeroot(nd+a),access(nd+b);splay(nd+b);
	return nd[b].sum;
}
int main()
{
	int a, b;
	init();
	for(M=read();M;M--)a=read(),b=read(),printf("%d\n",work(a,b));
	return 0;
}




评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值