UOJ #207. 共价大爷游长沙

#207. 共价大爷游长沙

链接:http://uoj.ac/problem/207

题意:给一棵树,要求支持加边、删边、询问一条边是否被所有路径覆盖。同时路径端点集合有加入与删除操作。

想法:

考虑一个点与其父亲边是否被一条路径经过。
就是该路径的一端在其子树中,另一端不在。
就是其子树中一条路径的端点出现次数为奇数。随机给一条路径两端一个权值(错误概率为$\frac{n^2}{2^w}$),然后如果一个节点子树xor值等于当前路径xor值,其到父亲边就是可行的边。

然后便是LCT维护加边,删边,子树xor值。

Code $O(n \log n)$

#include < cstdio >
#include < cstdlib >
#include < ctime >

#define gec     getchar
#define FILE(F) freopen(F".in","r",stdin),freopen(F".out","w",stdout)
#define DEBUG   fprintf(stderr,"Passing [%s] in Line (%d)\n",__FUNCTION__,__LINE__)

typedef long long ll;
typedef unsigned long long ull;
template < typename T >
inline void read(T &x)
{
	x=0;bool f=0; char c=gec();
	for(;c<‘0‘||c>‘9‘;c=gec())f=(c==‘-‘);
	for(;c>=‘0‘&&c<=‘9‘;c=gec())x=x*10+c-‘0‘;
	x=f?-x:x;
}

const int MAXN(100010),MAXM(300010);
int n,m,u,v,x,y,X[MAXM],Y[MAXM],tp;
ull QSQ,T[MAXM];

ull Random()
{
	return rand()*1ull<<30|rand();;
}

namespace Link_Cut_Tree
{
	struct LCT
	{
		int nx[2],fa,rev;
		ull val,sum,light;
		//val:该节点xor值; sum:其子树xor值,light:其轻儿子xor值
	}tr[MAXN];

	void look(int x)
	{
		fprintf(stderr,"x%d\n",x);
		fprintf(stderr,"nx[0]%d nx[1]%d fa%d\n",tr[x].nx[0],tr[x].nx[1],tr[x].fa);
	}

	void swap(int &x,int &y){int t(x);x=y;y=t;}
	int which(int x){if(tr[tr[x].fa].nx[0]==x)return 0;if(tr[tr[x].fa].nx[1]==x)return 1;return -1;}

	void update(int x)
	{
		tr[x].sum=tr[x].val^tr[x].light;
		if(tr[x].nx[0])tr[x].sum^=tr[tr[x].nx[0]].sum;
		if(tr[x].nx[1])tr[x].sum^=tr[tr[x].nx[1]].sum;
	}

	void push(int x)
	{
		if(!tr[x].rev)return ;
		swap(tr[x].nx[0],tr[x].nx[1]);
		tr[tr[x].nx[0]].rev^=1;
		tr[tr[x].nx[1]].rev^=1;
		tr[x].rev=0;
	}

	void rotate(int x)
	{
		int fa=tr[x].fa,fafa=tr[fa].fa,xd=which(x),fd=which(fa);
		tr[tr[x].nx[xd^1]].fa=fa;
		tr[fa].nx[xd]=tr[x].nx[xd^1];
		tr[x].nx[xd^1]=fa;tr[fa].fa=x;
		tr[x].fa=fafa;if(~fd)tr[fafa].nx[fd]=x;
		update(fa);
	}

	int st[MAXN],top;
	void splay(int x)
	{
		st[top=1]=x;
		for(int t=x;~which(t);t=tr[t].fa)st[++top]=tr[t].fa;
		while(top)push(st[top--]);
		while(~which(x))
		{
			int fa=tr[x].fa;
			if(~which(fa)) rotate( which(x)^which(fa)? fa : x );
			rotate(x);
		}
		update(x);
	}

	void access(int x)
	{
		for(int t=0;x;t=x,x=tr[x].fa)
		{
			splay(x);
			int Now=tr[x].nx[1];
			if(Now)tr[x].light^=tr[Now].sum;
			if(t  )tr[x].light^=tr[t  ].sum;
			tr[x].nx[1]=t; update(x);
		}
	}

	void make_root(int x)
	{
		access(x); splay(x); tr[x].rev^=1;
	}

	void link(int u,int v)
	{
		make_root(u); access(v); splay(v);
		tr[u].fa=v; tr[v].light^=tr[u].sum; update(v);
	}

	void cut(int u,int v)
	{
		make_root(u); access(v); splay(u);
		tr[u].nx[1]=0; tr[v].fa=0; update(u);
	}

	void Change(int x,ull D)
	{
		access(x); splay(x);
		tr[x].val^=D; update(x);
	}

	bool Que(int u,int v)
	{
		make_root(u); access(v); splay(u);
		return tr[v].sum==QSQ;
	}

}using namespace Link_Cut_Tree;

int main()
{
#ifndef ONLINE_JUDGE
	FILE("C");
#endif
	int id;read(id); srand(19260817);
	read(n);read(m);
	for(int i=1;i<n;i++)
	{
		read(u);read(v);
		link(u,v);
	}
	for(int ty,i=1;i<=m;i++)
	{
		read(ty);
		if(ty==1)
		{
			read(x);read(y); cut(x,y);
			read(u);read(v); link(u,v);
		}else
		if(ty==2)
		{
			++tp;
			read(X[tp]);read(Y[tp]);
			T[tp]=Random();QSQ^=T[tp];
			Change(X[tp],T[tp]); Change(Y[tp],T[tp]);
		}else
		if(ty==3)
		{
			read(x);QSQ^=T[x];
			Change(X[x],T[x]); Change(Y[x],T[x]);
		}else
		{
			read(x);read(y);
			printf(Que(x,y)?"YES\n":"NO\n");
		}
	}
	return 0;
}
时间: 2024-08-06 15:08:46

UOJ #207. 共价大爷游长沙的相关文章

#207. 共价大爷游长沙

题目描述 http://uoj.ac/problem/207 题解 因为这道题有删边和加边的操作,所以我们不能再链上操作,只能在点上操作. 考虑一些正确性玄学的算法. 我们给每一次链加随机一个权值,这样对于每次询问就查一下这条边分成的两块中的权值异或和是否等于当前所有链的权值异或和即可. 代码 #include<iostream> #include<cstdio> #include<cstdlib> #define ls ch[x][0] #define rs ch[x

@uoj - [email&#160;protected] 共价大爷游长沙

目录 @[email protected] @[email protected] @accepted [email protected] @[email protected] @[email protected] 火车司机出秦川,跳蚤国王下江南,共价大爷游长沙.每个周末,勤劳的共价大爷都会开车游历长沙市. 长沙市的交通线路可以抽象成为一个 n 个点 n?1 条边的无向图,点编号为 1 到 n,任意两点间均存在恰好一条路径,显然两个点之间最多也只会有一条边相连.有一个包含一些点对 (x,y) 的可

【uoj207】 共价大爷游长沙

http://uoj.ac/problem/207 (题目链接) 题意 给出一棵无根树,4种操作:在路径集合中加入一条路径,在路径集合中删除一条路径,删一条边加一条边,查询一条边是否被集合中所有路径经过. Solution 将路径端点同时异或上一个值,那么如果一条路径被经过,那么它的子树中点的异或和一定等于所有路径的异或和. 考虑如何用LCT维护这种可加减的子树信息. 对于询问,我们将询问的点access一下,那么它的所有虚儿子就是它在真实的树中的所有儿子了. 对于会使轻重边切换的操作:acce

UOJ207:共价大爷游长沙

题面 UOJ Sol 神题 给每个点对随机一个权值,把这两个点的权值异或上这个随机的值 用\(LCT\)维护子树信息,若子树异或和为所有点对的异或和那么就是答案 大常数代码 # include <bits/stdc++.h> # define RG register # define IL inline # define Fill(a, b) memset(a, b, sizeof(a)) using namespace std; typedef long long ll; const int

[UOJ207]共价大爷游长沙

如果每次加入点对$(x,y)$,就给它一个随机的权值$v$,把两个点的点权都异或$v$,查询$(x,y)$的时候,只要把$x$硬点为根,以$y$为根的子树的异或和等于当前所有的异或和,那么很大概率就是正确的(每对点刚好有一个在$y$的子树中) 所以直接用lct维护即可,因为维护了虚边信息,所以link时两边都要makeroot(不然没法更新splay里的祖先的信息),cut时不改虚边信息 splay之前一定要记得一路pushdown啊啊啊啊啊啊! #include<stdio.h> #incl

[uoj207]共价大爷游长沙——lct

题目大意: 给定一棵树和一些路径的集合,同时树和集合都可以支持在线修改,然后在某一时刻求一条边是否被所有集合之中的路径给覆盖. 思路: 考虑一个简单的思路,每当添加一条路径,我们就把在这条路径上的所有边的边权都异或上一个随机值,然后对于任意一条需要询问的边,我们只需要判断它的权值是否等于目前所有的路径的权值的异或和即可. 当我们的权值很大的时候,出错的概率很低,所以可以近似为正确的. 但是树的形态也需要动态修改,这就说明一条路径在不同的版本中,它所代表的边是不一样的,这就很麻烦. 但是仔细观察一

【LCT维护子树信息】uoj207 共价大爷游长沙

这道题思路方面就不多讲了,主要是通过这题学一下lct维护子树信息. lct某节点u的子树信息由其重链的一棵splay上信息和若干轻儿子子树信息合并而成. splay是有子树结构的,可以在rotate,access的时候由儿子update到父亲,而轻儿子的信息update不上来,需要另外记一下. 记sum[x]为我们要求的子树信息,xu[x]为x的轻儿子的子树信息. (即,xu[x]由轻儿子的sum更新,sum[x]由xu[x]和splay子树上的儿子的sum更新. 这样我们就可以完整地用lct维

2019寒假小记

Preface 菜鸡HL终于艰难地熬过了初三的一个学期迎来了寒假. 但是由于某些不可言说的政策问题所以他要没高中读了,只能开始准备中考. 所以这个寒假可能并不像想象中的那样可以大量刷题好吧题还是要做的 希望\(ZJOI\)能\(Keep\ good\ luck\)吧,进不了前\(48\)就要考虑退役了. 1-26 早上搬东西发成绩单听老杨浩BB,搞到\(10\)点种才放. ZLX很早就走了,心想有点不对,肯定是去机房了. 所以推测HW的人都去机房了,于是我决定也去机房玩玩. 把东西拿到老爸车上就

成都七中

Day1T1感觉是一个DP貌似还是一个不太会的数位DP暴力...T2暴力然而刚刚学还是不太会还是暴力,感觉能拿点分 下午是LCT不过在之前讲了一大堆的势能分析然后O(1)秒掉了BZOJ上的一道题感觉还是挺6的然后就是LCT板子还不是太熟大概只能听一下处理LCT问题的思想 晚上打了BZOJ上的大融合本来还想过了共价大爷游长沙不过splay的标记不会打就看了标程,看懂了差不多也回宿舍了.. Day2T1就是暴力搞一搞加一个剪枝就A了然而我并没有减掉T2 扔了一个贪心上去T3 n^2暴力-->40 下