题目均来自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;
}
再下一题
乍一看好像不是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;
}
再再下一题
题目乍一看好像无从下手,既给的边权,而且一条边还有两个边权???
其实很简单,我们只需要将边也看成点就行了。
但是一个点有两个边权怎么搞呢
我们先将边按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;
}