【LCT裸题】

题目均来自ZZQ博客:LCT裸题泛做

BZOJ2049
非常裸的LCT,连边断边询问连通性,并查集也可做。
(有点卡常)

#include <bits/stdc++.h>
#define sc(n) scanf("%d",&n)
#define pt(n) printf("%d\n",n)
#define rep(i,a,b) for(int i=a;i<=b;i++)
#define vi vector<int>
#define vl vector<long long>
#define pb push_back
#define INF 0x3f3f3f3f
using namespace std;
const int maxn = 1e5+7;
int fa[maxn],ch[maxn][2],st[maxn],rev[maxn];
bool nroot(int x)
{
	return ch[fa[x]][0]==x || ch[fa[x]][1] == x;
}
void pushr(int x)
{
	swap(ch[x][0],ch[x][1]);
	rev[x] ^= 1;
}
void pushdown(int x)
{
	if(rev[x])
	{
		if(ch[x][0]) pushr(ch[x][0]);
		if(ch[x][1]) pushr(ch[x][1]);
		rev[x] = 0;
	}
}
void rotate(int x)
{
	int y = fa[x],z = fa[y], k = ch[y][1]==x,w = ch[x][!k];
	if(nroot(y)) ch[z][ch[z][1]==y]=x;
	ch[x][!k] = y,ch[y][k] = w;
	if(w) fa[w] = y;
	fa[y] = x,fa[x] = z;
}
void splay(int x)
{
	int y = x,top = 0;
	st[++top] = y;
	while(nroot(y)) st[++top] = y = fa[y];
	while(top) pushdown(st[top--]);
	while(nroot(x))
	{
		y = fa[x];
		int z = fa[y];
		if(nroot(y)) rotate((ch[y][0]==x)^(ch[z][0]==y)?x:y);
		rotate(x);
	}
}
void access(int x)
{
	for(int y=0;x;x=fa[y=x])
	{
		splay(x);
		ch[x][1] = y;
	}
}
void makeroot(int x)
{
	access(x);
	splay(x);
	pushr(x);
}
int findroot(int x)
{
	access(x);
	splay(x);
	while(ch[x][0])
	{
		pushdown(x);
		x = ch[x][0];
	}
	splay(x);
	return x;
}
void split(int x,int y)
{
	makeroot(x);
	access(y);
	splay(y);
}
void link(int x,int y)
{
	makeroot(x);
	if(findroot(y)!=x) fa[x] = y;
}
void cut(int x,int y)
{
	makeroot(x);
	if(findroot(y)==x && fa[y]==x && !ch[y][0])
	{
		fa[y] = ch[x][1] = 0;
	}
}
char s[20];
int main()
{
	int n,m;
	scanf("%d%d",&n,&m);
	for(int i=0;i<m;i++)
	{
		int x,y;
		scanf("%s%d%d",s,&x,&y);
		if(s[0]=='C') link(x,y);
		if(s[0]=='D') cut(x,y);
		if(s[0]=='Q')
		{
			if(findroot(x)==findroot(y)) puts("Yes");
			else puts("No");
		}
	}
	return 0;
}

下一题
BZOJ1036
询问一颗树上路径的点权最大值/权值和。

树剖裸题,但是LCT也可做,但是LCT比较慢(因为splay很慢)。

Attention
(1) 建树的时候要先赋好权值,然后再连边。
(2) rotate写错了
(3) 连边少连了一条…

调了半小时,原来是建树建错了…

#include <bits/stdc++.h>
#define INF 0x3f3f3f3f
using namespace std;
const int maxn = 1e5+7;
int fa[maxn],ch[maxn][2],v[maxn],sum[maxn],mx[maxn],st[maxn];
bool rev[maxn];
int ex[maxn],ey[maxn];
bool nroot(int x)
{
	return ch[fa[x]][0]==x || ch[fa[x]][1]==x;
}
void pushup(int x)
{
	sum[x] = sum[ch[x][0]]+sum[ch[x][1]]+v[x];
	mx[x] = max(max(mx[ch[x][0]],mx[ch[x][1]]),v[x]);
}
void pushr(int x)
{
	swap(ch[x][0],ch[x][1]);
	rev[x] ^= 1;
}
void pushdown(int x)
{
	if(rev[x])
	{
		if(ch[x][0]) pushr(ch[x][0]);
		if(ch[x][1]) pushr(ch[x][1]);
		rev[x] = 0;
	}
}
void rotate(int x)
{
	int y = fa[x], z = fa[y], k = ch[y][1]==x, w = ch[x][!k];
	if(nroot(y)) ch[z][ch[z][1]==y]=x;
	ch[x][!k] = y, ch[y][k] = w;
	if(w) fa[w] = y;
	fa[y] = x, fa[x] = z;
	pushup(y);
}
void splay(int x)
{
	int y = x,top = 0;
	st[++top] = y;
	while(nroot(y)) st[++top] = y = fa[y];
	while(top) pushdown(st[top--]);
	while(nroot(x))
	{
		y = fa[x];
		int z = fa[y];
		if(nroot(y)) rotate((ch[y][0]==x)^(ch[z][0]==y)?x:y);
		rotate(x);
	}
	pushup(x);
}
void access(int x)
{
	for(int y=0;x;x=fa[y=x])
	{
		splay(x);
		ch[x][1] = y;
		pushup(x);
	}
}
void makeroot(int x)
{
	access(x);
	splay(x);
	pushr(x);
}
int findroot(int x)
{
	access(x);
	splay(x);
	while(ch[x][0])
	{
		pushdown(x);
		x = ch[x][0];
	}
	splay(x);
	return x;
}
void split(int x,int y)
{
	makeroot(x);
	access(y);
	splay(y);
}
void link(int x,int y)
{
	makeroot(x);
	if(findroot(y)!=x) fa[x] = y;
}
void cut(int x,int y)
{
	makeroot(x);
	if(findroot(y)==x && fa[y]==x && !ch[y][0])
	{
		fa[y] = ch[x][1] = 0;
		pushup(x);
	}
}
char s[20];
int main()
{
	mx[0] = -INF;
	int n,q;
	scanf("%d",&n);
	for(int i=0;i<n-1;i++)
	{
		int x,y;
		scanf("%d%d",ex+i,ey+i);
	}
	for(int i=1;i<=n;i++) scanf("%d",v+i),sum[i] = mx[i] = v[i];
	for(int i=0;i<n-1;i++) link(ex[i],ey[i]);
	scanf("%d",&q);
	while(q--)
	{
		int a,b;
		scanf("%s%d%d",s,&a,&b);
		if(s[0]=='C')
		{
			splay(a);
			v[a] = b;
		}
		else
		{
			split(a,b);
			if(s[1]=='M') printf("%d\n",mx[b]);
			else printf("%d\n",sum[b]);
		}
	} 
	return 0;
}

再下一题

BZOJ2002

乍一看好像不是LCT的题目,但是仔细分析之后就发现:弹力弹羊实际上就是两点之间连边,如果超过n就连n+1,表示跳出了这个长度。
改变某一个点的弹力其实就是把旧边删掉然后连上新边,询问从j点跳出去要跳几次其实就是询问(j,n+1)的大小,答案显然要减一。

#include <bits/stdc++.h>
using namespace std;
const int maxn = 1e6+7;
int fa[maxn],ch[maxn][2],siz[maxn],st[maxn],rev[maxn];
int ki[maxn];
bool nroot(int x)
{
	return ch[fa[x]][0]==x || ch[fa[x]][1]==x;
}
void pushup(int x)
{
	siz[x] = siz[ch[x][0]] + siz[ch[x][1]] + 1;
}
void pushr(int x)
{
	swap(ch[x][0],ch[x][1]);
	rev[x] ^= 1;
}
void pushdown(int x)
{
	if(rev[x])
	{
		if(ch[x][0]) pushr(ch[x][0]);
		if(ch[x][1]) pushr(ch[x][1]);
		rev[x] = 0;
	}
}
void rotate(int x)
{
	int y = fa[x], z = fa[y], k = ch[y][1]==x, w = ch[x][!k];
	if(nroot(y)) ch[z][ch[z][1]==y]=x;
	ch[x][!k] = y, ch[y][k] = w;
	if(w) fa[w] = y;
	fa[y] = x,fa[x] = z;
	pushup(y);
}
void splay(int x)
{
	int y = x,top = 0;
	st[++top] = y;
	while(nroot(y)) st[++top] = y = fa[y];
	while(top) pushdown(st[top--]);
	while(nroot(x)) 
	{
		y = fa[x];
		int z = fa[y];
		if(nroot(y)) rotate((ch[y][0]==x)^(ch[z][0]==y)?x:y);
		rotate(x);
	}
	pushup(x);
}
void access(int x)
{
	for(int y=0;x;x=fa[y=x])
	{
		splay(x);
		ch[x][1] = y;
		pushup(x);
	}
}
void makeroot(int x)
{
	access(x);
	splay(x);
	pushr(x);
}
int findroot(int x)
{
	access(x);
	splay(x);
	while(ch[x][0])
	{
		pushdown(x);
		x = ch[x][0];
	}
	splay(x);
	return x;
}
void split(int x,int y)
{
	makeroot(x);
	access(y);
	splay(y);
}
void link(int x,int y)
{
	makeroot(x);
	if(findroot(y)!=x) fa[x] = y;
}
void cut(int x,int y)
{
	makeroot(x);
	if(findroot(y)==x && fa[y]==x && !ch[y][0])
	{
		fa[y] = ch[x][1] = 0;
		pushup(x);
	}
}
int main()
{
	int n,q;
	scanf("%d",&n);
	for(int i=1;i<=n;i++)
	{
		scanf("%d",ki+i);
		link(i,min(ki[i]+i,n+1));
	}
	scanf("%d",&q);
	while(q--)
	{
		int a,b;
		scanf("%d%d",&a,&b);
		b++;
		if(a==1)
		{
			split(n+1,b);
			printf("%d\n",siz[b]-1);
		}
		else
		{
			int c;
			scanf("%d",&c);
			cut(b,min(b+ki[b],n+1));
			ki[b] = c;
			link(b,min(b+ki[b],n+1));
		}
	}
	return 0;
}

再再下一题

BZOJ3669

题目乍一看好像无从下手,既给的边权,而且一条边还有两个边权???
其实很简单,我们只需要将边也看成点就行了。
但是一个点有两个边权怎么搞呢
我们先将边按a从小到大排序,然后枚举边,这时候a已经是最小的值了,我们已经不需要考虑它了,
如果对于a的边(x,y),在没加入这条边之前已经连通了,且图里面的最大的b都比当前的b小,就跳过这个边,否则我们将那个较大的b的边删掉,然后连上新的边,更新一下最小的答案即可。

#include <bits/stdc++.h>
#define INF 0x3f3f3f3f
using namespace std;
const int maxn = 2e6+7;
struct node
{
	int x,y;
	int a,b;
	bool operator<(const node &rhs)const
	{
		return a<rhs.a;
	}
}e[maxn];
int fa[maxn],ch[maxn][2],mxid[maxn],v[maxn],st[maxn],rev[maxn];
bool nroot(int x)
{
	return ch[fa[x]][0]==x || ch[fa[x]][1]==x;
}
void pushup(int x)
{
	if(v[mxid[ch[x][0]]]>v[mxid[ch[x][1]]] && v[mxid[ch[x][0]]]>v[x]) mxid[x] = mxid[ch[x][0]];
	else if(v[mxid[ch[x][1]]]>v[x]) mxid[x] = mxid[ch[x][1]];
	else mxid[x] = x;
}
void pushr(int x)
{
	swap(ch[x][0],ch[x][1]);
	rev[x] ^= 1;
}
void pushdown(int x)
{
	if(rev[x])
	{
		if(ch[x][0]) pushr(ch[x][0]);
		if(ch[x][1]) pushr(ch[x][1]);
		rev[x] = 0;
	}
}
void rotate(int x)
{
	int y = fa[x],z=fa[y],k=ch[y][1]==x,w=ch[x][!k];
	if(nroot(y)) ch[z][ch[z][1]==y]=x;
	ch[x][!k]=y,ch[y][k]=w;
	if(w) fa[w] = y;
	fa[y] = x,fa[x] = z;
	pushup(y);
}
void splay(int x)
{
	int y = x,top = 0;
	st[++top] = y;
	while(nroot(y)) st[++top] = y = fa[y];
	while(top) pushdown(st[top--]);
	while(nroot(x))
	{
		y = fa[x];
		int z = fa[y];
		if(nroot(y)) rotate((ch[y][0]==x)^(ch[z][0]==y)?x:y);
		rotate(x);
	}
	pushup(x);
}
void access(int x)
{
	for(int y=0;x;x=fa[y=x])
	{
		splay(x);
		ch[x][1] = y;
		pushup(x);
	}
}
void makeroot(int x)
{
	access(x);
	splay(x);
	pushr(x);
}
int findroot(int x)
{
	access(x);
	splay(x);
	while(ch[x][0])
	{
		pushdown(x);
		x = ch[x][0];
	}
	splay(x);
	return x;
}
void split(int x,int y)
{
	makeroot(x);
	access(y);
	splay(y);
}
void link(int x,int y)
{
	makeroot(x);
	if(findroot(y)!=x) fa[x] = y;
}
void cut(int x,int y)
{
	makeroot(x);
	if(findroot(y)==x && fa[y]==x && !ch[y][0])
	{
		fa[y] = ch[x][1] = 0;
		pushup(x);
	}
}
int main()
{
	int n,m;
	scanf("%d%d",&n,&m);
	for(int i=1;i<=m;i++)
	{
		scanf("%d%d%d%d",&e[i].x,&e[i].y,&e[i].a,&e[i].b);
	}
	sort(e+1,e+1+m);
	int ans = INF;
	for(int i=1;i<=m;i++)
	{
		int x = e[i].x,y = e[i].y,a = e[i].a,b = e[i].b;
		v[i+n] = b;
		if(findroot(x)==findroot(y))
		{
			split(x,y);
			int p = mxid[y];
			if(v[p]<=b) continue;
			cut(p,e[p-n].x);
			cut(p,e[p-n].y);
		}
		link(x,i+n);
		link(y,i+n);
		if(findroot(1)==findroot(n))
		{
			split(1,n);
			ans = min(ans,v[mxid[n]]+a);
		}
	}
	if(ans!=INF) printf("%d\n",ans);
	else printf("-1\n");
	return 0;
}
发布了159 篇原创文章 · 获赞 13 · 访问量 1万+

猜你喜欢

转载自blog.csdn.net/KIKO_caoyue/article/details/99847126
LCT