类比树剖,树剖是通过静态地把一棵树剖成若干条链然后用一种支持区间操作的数据结构维护(比如线段树、树状数组),而LCT是动态地去处理这个问题。
不少人都知道树剖用线段树维护,而LCT用\(splay\)维护。实际上同一条重链上的所有点才会被放在一棵\(splay\)中,而我们需要同时处理这若干条重链对应的若干棵\(splay\)之间的关系。因为一条重链上的每个点的深度互异,所以\(splay\)以深度\(dep\)为关键字。
我们规定一棵\(splay\)的根的\(fa\)为这条重链链顶节点在原树中的父亲节点。(前面的那个\(fa\)指的是\(splay\)中的\(fa\))
而显然认了这个父亲之后,父亲不会认这个儿子:很简单,这两个点不在同一条重链上,所以父亲的左右儿子一定没有它。由此可以写一个判断一个点是不是根节点(当前\(splay\)的根节点)的函数:
bool isroot(int x){
return ls[fa[x]]!=x&&rs[fa[x]]!=x;
}
先讲LCT最重要的一个操作:\(access(x)\),表示把\(x\)节点到\(x\)所在树(连通块)的根节点之间的路径全部变成重路径。
void access(int x){
for (int y=0;x;y=x,x=fa[x])
splay(x),rs[x]=y,pushup(x);
}
相当于是说要把这些在路径上的点全部搞到一棵\(splay\)里面去。
从下往上做,每次把\(x\)splay到当前\(splay\)的根后,这个\(x\)就会处于一个很微妙的位置:它的右儿子\(rs\)应该会接上这条重链下面的一部分,因为这条重链下面一部分的深度显然会大于\(x\)。而所谓“下面的一部分”就是我们上一个处理的\(x\),这也就是为什么y=x,x=fa[x]
了。
最开始的\(x\)的下面不应该接上任何东西,所以\(y\)的初值赋为0。
再就是另一个基本操作:\(makeroot(x)\),表示把\(x\)节点设为\(x\)所在树(连通块)的根节点。
我们先把\(x\)access了,即连接了\(x\)与根节点,然后再把\(x\)splay到根,那么显然这个\(x\)肯定会是\(splay\)中的最后一个元素(因为它深度最大),所以我们在\(x\)这里打一个\(rev\)标记(\(splay\)区间翻转操作)即可。
void makeroot(int x){
access(x);splay(x);rev[x]^=1;
}
剩下的操作就很简单了。
\(findroot(x)\),找到\(x\)节点所在树(连通块)的根节点。用这个操作可以维护连通性。
\(findroot(x)=access(x)+splay(x)+\)找到最左边(深度最小)的那个节点。
int findroot(int x){
access(x);splay(x);
while (ls[x]) x=ls[x];
return x;
}
\(split(x,y)\),抠出\(x\)到\(y\)的路径,抠完以后\(y\)是\(splay\)的根。
\(split(x,y)=makeroot(x)+access(y)+splay(y)\)
void split(int x,int y){
makeroot(x);access(y);splay(y);
}
\(cut(x,y)\),砍断\(x\)到\(y\)的边。
\(cut(x,y)=split(x,y)+\)x的父亲和y的左儿子置0。
(split之后这一棵\(splay\)中就只剩\(x\)和\(y\)两个点了,所以才可以这么搞)
void cut(int x,int y){
split(x,y);fa[x]=ls[y]=0;
}
\(link(x,y)\),连接\(x\)到\(y\)的边。
\(cut(x,y)=makeroot(x)+fa[x]=y\)
void link(int x,int y){
makeroot(x);fa[x]=y;
}
【模板】LCT
路径异或和+动态连边删边+单点修改。
#include<cstdio>
#include<algorithm>
using namespace std;
const int _ = 300005;
int n,m,opt,a,b,fa[_],ls[_],rs[_],sum[_],val[_],rev[_],Stack[_],top;
int gi()
{
int x=0,w=1;char ch=getchar();
while ((ch<'0'||ch>'9')&&ch!='-') ch=getchar();
if (ch=='-') w=0,ch=getchar();
while (ch>='0'&&ch<='9') x=(x<<3)+(x<<1)+ch-'0',ch=getchar();
return w?x:-x;
}
void pushup(int x){sum[x]=sum[ls[x]]^sum[rs[x]]^val[x];}
bool isroot(int x){return ls[fa[x]]!=x&&rs[fa[x]]!=x;}
void pushdown(int x){if (!rev[x]) return;swap(ls[x],rs[x]);rev[ls[x]]^=1;rev[rs[x]]^=1;rev[x]=0;}
void R_rotate(int x)
{
int y=fa[x],z=fa[y];
ls[y]=rs[x];
if (rs[x]) fa[rs[x]]=y;
fa[x]=z;
if (!isroot(y)) if (y==ls[z]) ls[z]=x;else rs[z]=x;
rs[x]=y;fa[y]=x;
pushup(y);
}
void L_rotate(int x)
{
int y=fa[x],z=fa[y];
rs[y]=ls[x];
if (ls[x]) fa[ls[x]]=y;
fa[x]=z;
if (!isroot(y)) if (y==ls[z]) ls[z]=x;else rs[z]=x;
ls[x]=y;fa[y]=x;
pushup(y);
}
void splay(int x)
{
Stack[++top]=x;
for (int i=x;!isroot(i);i=fa[i])
Stack[++top]=fa[i];
while (top) pushdown(Stack[top--]);
while (!isroot(x))
{
int y=fa[x],z=fa[y];
if (isroot(y))
if (x==ls[y]) R_rotate(x);
else L_rotate(x);
else
if (y==ls[z])
if (x==ls[y]) R_rotate(y),R_rotate(x);
else L_rotate(x),R_rotate(x);
else
if (x==ls[y]) R_rotate(x),L_rotate(x);
else L_rotate(y),L_rotate(x);
}
pushup(x);
}
void access(int x){for (int y=0;x;y=x,x=fa[x]) splay(x),rs[x]=y,pushup(x);}
void makeroot(int x){access(x);splay(x);rev[x]^=1;}
int findroot(int x){access(x);splay(x);while (ls[x]) x=ls[x];return x;}
void split(int x,int y){makeroot(x);access(y);splay(y);}
void cut(int x,int y){split(x,y);fa[x]=ls[y]=0;}
void link(int x,int y){makeroot(x);fa[x]=y;}
int main()
{
n=gi();m=gi();
for (int i=1;i<=n;i++) val[i]=gi();
while (m--)
{
opt=gi();a=gi();b=gi();
if (opt==0)
split(a,b),printf("%d\n",sum[b]);
if (opt==1)
if (findroot(a)!=findroot(b))
link(a,b);
if (opt==2)
if (findroot(a)==findroot(b))
cut(a,b);
if (opt==3)
access(a),splay(a),val[a]=b,pushup(a);
}
return 0;
}
[SDOI2008]Cave 洞穴勘测
LCT维护连通性。
#include<cstdio>
#include<algorithm>
using namespace std;
const int _ = 10005;
int n,m,a,b,fa[_],ls[_],rs[_],rev[_],Stack[_],top;
char s[_];
int gi()
{
int x=0,w=1;char ch=getchar();
while ((ch<'0'||ch>'9')&&ch!='-') ch=getchar();
if (ch=='-') w=0,ch=getchar();
while (ch>='0'&&ch<='9') x=(x<<3)+(x<<1)+ch-'0',ch=getchar();
return w?x:-x;
}
bool isroot(int x){return ls[fa[x]]!=x&&rs[fa[x]]!=x;}
void pushdown(int x){if (!rev[x]) return;swap(ls[x],rs[x]);rev[ls[x]]^=1;rev[rs[x]]^=1;rev[x]=0;}
void R_rotate(int x)
{
int y=fa[x],z=fa[y];
ls[y]=rs[x];
if (rs[x]) fa[rs[x]]=y;
fa[x]=z;
if (!isroot(y)) if (y==ls[z]) ls[z]=x;else rs[z]=x;
rs[x]=y;fa[y]=x;
}
void L_rotate(int x)
{
int y=fa[x],z=fa[y];
rs[y]=ls[x];
if (ls[x]) fa[ls[x]]=y;
fa[x]=z;
if (!isroot(y)) if (y==ls[z]) ls[z]=x;else rs[z]=x;
ls[x]=y;fa[y]=x;
}
void splay(int x)
{
Stack[++top]=x;
for (int i=x;!isroot(i);i=fa[i])
Stack[++top]=fa[i];
while (top) pushdown(Stack[top--]);
while (!isroot(x))
{
int y=fa[x],z=fa[y];
if (isroot(y))
if (x==ls[y]) R_rotate(x);
else L_rotate(x);
else
if (y==ls[z])
if (x==ls[y]) R_rotate(y),R_rotate(x);
else L_rotate(x),R_rotate(x);
else
if (x==ls[y]) R_rotate(x),L_rotate(x);
else L_rotate(y),L_rotate(x);
}
}
void access(int x){for (int y=0;x;y=x,x=fa[x]) splay(x),rs[x]=y;}
void makeroot(int x){access(x);splay(x);rev[x]^=1;}
int findroot(int x){access(x);splay(x);while (ls[x]) x=ls[x];return x;}
void split(int x,int y){makeroot(x);access(y);splay(y);}
void cut(int x,int y){split(x,y);ls[y]=fa[x]=0;}
void link(int x,int y){makeroot(x),fa[x]=y;}
int main()
{
n=gi();m=gi();
while (m--)
{
scanf("%s",s);a=gi();b=gi();
if (s[0]=='Q')
puts(findroot(a)==findroot(b)?"Yes":"No");
if (s[0]=='C')
link(a,b);
if (s[0]=='D')
cut(a,b);
}
return 0;
}
[HNOI2010]BOUNCE 弹飞绵羊
LCT动态维护深度。
其实就是求到根节点路径上的size-1。
对于每一个会弹飞的点,连到建立的超级根上去。这样就保证始终是一棵树。(主要是为了方便\(split\))
#include<cstdio>
#include<algorithm>
using namespace std;
const int _ = 200005;
int n,m,opt,pos,fa[_],ls[_],rs[_],sz[_],nxt[_],rev[_],Stack[_],top;
int gi()
{
int x=0,w=1;char ch=getchar();
while ((ch<'0'||ch>'9')&&ch!='-') ch=getchar();
if (ch=='-') w=0,ch=getchar();
while (ch>='0'&&ch<='9') x=(x<<3)+(x<<1)+ch-'0',ch=getchar();
return w?x:-x;
}
void pushup(int x){sz[x]=sz[ls[x]]+sz[rs[x]]+1;}
bool isroot(int x){return ls[fa[x]]!=x&&rs[fa[x]]!=x;}
void pushdown(int x){if (!rev[x]) return;swap(ls[x],rs[x]);rev[ls[x]]^=1;rev[rs[x]]^=1;rev[x]=0;}
void R_rotate(int x)
{
int y=fa[x],z=fa[y];
ls[y]=rs[x];
if (rs[x]) fa[rs[x]]=y;
fa[x]=z;
if (!isroot(y)) if (y==ls[z]) ls[z]=x;else rs[z]=x;
rs[x]=y;fa[y]=x;
pushup(y);
}
void L_rotate(int x)
{
int y=fa[x],z=fa[y];
rs[y]=ls[x];
if (ls[x]) fa[ls[x]]=y;
fa[x]=z;
if (!isroot(y)) if (y==ls[z]) ls[z]=x;else rs[z]=x;
ls[x]=y;fa[y]=x;
pushup(y);
}
void splay(int x)
{
Stack[++top]=x;
for (int i=x;!isroot(i);i=fa[i])
Stack[++top]=fa[i];
while (top) pushdown(Stack[top--]);
while (!isroot(x))
{
int y=fa[x],z=fa[y];
if (isroot(y))
if (x==ls[y]) R_rotate(x);
else L_rotate(x);
else
if (y==ls[z])
if (x==ls[y]) R_rotate(y),R_rotate(x);
else L_rotate(x),R_rotate(x);
else
if (x==ls[y]) R_rotate(x),L_rotate(x);
else L_rotate(y),L_rotate(x);
}
pushup(x);
}
void access(int x){for (int y=0;x;y=x,x=fa[x]) splay(x),rs[x]=y,pushup(x);}
void makeroot(int x){access(x);splay(x);rev[x]^=1;}
int findroot(int x){access(x);splay(x);while (ls[x]) x=ls[x];return x;}
void split(int x,int y){makeroot(x);access(y);splay(y);}
void cut(int x,int y){split(x,y);ls[y]=fa[x]=0;}
void link(int x,int y){makeroot(x);fa[x]=y;}
int main()
{
n=gi();
for (int i=1;i<=n;i++)
{
nxt[i]=min(i+gi(),n+1);
link(i,nxt[i]);
}
m=gi();
while (m--)
{
opt=gi();pos=gi()+1;
if (opt==1)
split(n+1,pos),printf("%d\n",sz[pos]-1);
else
{
cut(pos,nxt[pos]);
nxt[pos]=min(pos+gi(),n+1);
link(pos,nxt[pos]);
}
}
return 0;
}
[NOI2014]魔法森林
LCT维护最小生成树。
先把所有边按a值排序,然后维护以b为权值的最小生成树。
LCT处理边权信息:当做一条边是一个点,link就是连两次。
维护最小生成树:维护一段路径上的边权最大值和最大值的位置,新连一条边的时候讨论:
1、若要连接的两个点已经连通,那么判断边权最大值是否比要连的边的边权大。如果要大就把最大的那个位置断掉,新加入的边连上。若不大于就continue
。
2、若要连接的两个点未联通,那么直接连没话说。
然后这题差不多就做完了。
#include<cstdio>
#include<algorithm>
using namespace std;
#define inf 1e9
const int _ = 200005;
struct edge
{
int u,v,a,b;
bool operator < (const edge &zsy) const{return a==zsy.a?b<zsy.b:a<zsy.a;}
}e[_];
int n,m,fa[_],ls[_],rs[_],rev[_],val[_],mx[_],Stack[_],top,ans=inf;
int gi()
{
int x=0,w=1;char ch=getchar();
while ((ch<'0'||ch>'9')&&ch!='-') ch=getchar();
if (ch=='-') w=0,ch=getchar();
while (ch>='0'&&ch<='9') x=(x<<3)+(x<<1)+ch-'0',ch=getchar();
return w?x:-x;
}
void pushup(int x)
{
mx[x]=x;
mx[x]=val[mx[ls[x]]]>val[mx[x]]?mx[ls[x]]:mx[x];
mx[x]=val[mx[rs[x]]]>val[mx[x]]?mx[rs[x]]:mx[x];
}
bool isroot(int x){return ls[fa[x]]!=x&&rs[fa[x]]!=x;}
void pushdown(int x){if (!rev[x]) return;swap(ls[x],rs[x]);rev[ls[x]]^=1;rev[rs[x]]^=1;rev[x]=0;}
void R_rotate(int x)
{
int y=fa[x],z=fa[y];
ls[y]=rs[x];
if (rs[x]) fa[rs[x]]=y;
fa[x]=z;
if (!isroot(y)) if (y==ls[z]) ls[z]=x;else rs[z]=x;
rs[x]=y;fa[y]=x;
pushup(y);
}
void L_rotate(int x)
{
int y=fa[x],z=fa[y];
rs[y]=ls[x];
if (ls[x]) fa[ls[x]]=y;
fa[x]=z;
if (!isroot(y)) if (y==ls[z]) ls[z]=x;else rs[z]=x;
ls[x]=y;fa[y]=x;
pushup(y);
}
void splay(int x)
{
Stack[++top]=x;
for (int i=x;!isroot(i);i=fa[i])
Stack[++top]=fa[i];
while (top) pushdown(Stack[top--]);
while (!isroot(x))
{
int y=fa[x],z=fa[y];
if (isroot(y))
if (x==ls[y]) R_rotate(x);
else L_rotate(x);
else
if (y==ls[z])
if (x==ls[y]) R_rotate(y),R_rotate(x);
else L_rotate(x),R_rotate(x);
else
if (x==ls[y]) R_rotate(x),L_rotate(x);
else L_rotate(y),L_rotate(x);
}
pushup(x);
}
void access(int x){for (int y=0;x;y=x,x=fa[x]) splay(x),rs[x]=y,pushup(x);}
void makeroot(int x){access(x);splay(x);rev[x]^=1;}
int findroot(int x){access(x);splay(x);while (ls[x]) x=ls[x];return x;}
void split(int x,int y){makeroot(x);access(y);splay(y);}
void cut(int x,int y){split(x,y);fa[x]=ls[y]=0;}
void link(int x,int y){makeroot(x);fa[x]=y;}
int main()
{
n=gi();m=gi();
for (int i=n+1;i<=n+m;i++)
e[i].u=gi(),e[i].v=gi(),e[i].a=gi(),e[i].b=gi();
sort(e+n+1,e+n+m+1);
for (int i=n+1,u,v,a,b,pos;i<=n+m;i++)
{
u=e[i].u;v=e[i].v;a=e[i].a;b=e[i].b;
val[i]=b;mx[i]=i;
if (findroot(u)==findroot(v))
{
split(u,v);pos=mx[v];
if (val[pos]>b)
cut(pos,e[pos].u),cut(pos,e[pos].v);
else continue;
}
link(i,u);link(i,v);
if (findroot(1)==findroot(n))
split(1,n),ans=min(ans,a+val[mx[n]]);
}
printf("%d\n",ans==inf?-1:ans);
return 0;
}
[ZJOI2012]网络
很显然对于每个颜色维护一个LCT。
对于操作1:边直接暴力找(每个点连的边不超过20条),先判No such edge.
,然后判一下同色修改(等于没修改)直接Success.
。如果两个点新颜色的边有一个等于2就Error 1.
,再是若已经连通Error 2.
,最后修改并Success.
。
#include<cstdio>
#include<algorithm>
#include<map>
using namespace std;
const int _ = 10005;
struct edge{int to,next,col;}a[_*20];
int N,M,C,K,val[_],fa[10][_],ls[10][_],rs[10][_],s[10][_],tag[10][_],Stack[_],top,cnt,head[_];
struct node{int x,y;};
map<node,int>col;
int gi()
{
int x=0,w=1;char ch=getchar();
while ((ch<'0'||ch>'9')&&ch!='-') ch=getchar();
if (ch=='-') w=0,ch=getchar();
while (ch>='0'&&ch<='9') x=(x<<3)+(x<<1)+ch-'0',ch=getchar();
return w?x:-x;
}
void pushup(int x,int c){s[c][x]=max(max(s[c][ls[c][x]],s[c][rs[c][x]]),val[x]);}
bool isroot(int x,int c){return ls[c][fa[c][x]]!=x&&rs[c][fa[c][x]]!=x;}
void pushdown(int x,int c){if (!tag[c][x]) return;swap(ls[c][x],rs[c][x]);tag[c][ls[c][x]]^=1;tag[c][rs[c][x]]^=1;tag[c][x]=0;}
void R_rotate(int x,int c)
{
int y=fa[c][x],z=fa[c][y];
ls[c][y]=rs[c][x];
if (rs[c][x]) fa[c][rs[c][x]]=y;
fa[c][x]=z;
if (!isroot(y,c)) if (y==ls[c][z]) ls[c][z]=x;else rs[c][z]=x;
rs[c][x]=y;fa[c][y]=x;
pushup(y,c);
}
void L_rotate(int x,int c)
{
int y=fa[c][x],z=fa[c][y];
rs[c][y]=ls[c][x];
if (ls[c][x]) fa[c][ls[c][x]]=y;
fa[c][x]=z;
if (!isroot(y,c)) if (y==ls[c][z]) ls[c][z]=x;else rs[c][z]=x;
ls[c][x]=y;fa[c][y]=x;
pushup(y,c);
}
void splay(int x,int c)
{
Stack[++top]=x;
for (int i=x;!isroot(i,c);i=fa[c][i])
Stack[++top]=fa[c][i];
while (top) pushdown(Stack[top--],c);
while (!isroot(x,c))
{
int y=fa[c][x],z=fa[c][y];
if (isroot(y,c))
if (x==ls[c][y]) R_rotate(x,c);
else L_rotate(x,c);
else
if (y==ls[c][z])
if (x==ls[c][y]) R_rotate(y,c),R_rotate(x,c);
else L_rotate(x,c),R_rotate(x,c);
else
if (x==ls[c][y]) R_rotate(x,c),L_rotate(x,c);
else L_rotate(y,c),L_rotate(x,c);
}
pushup(x,c);
}
void access(int x,int c){for (int y=0;x;y=x,x=fa[c][x]) splay(x,c),rs[c][x]=y,pushup(x,c);}
void makeroot(int x,int c){access(x,c);splay(x,c);tag[c][x]^=1;}
int findroot(int x,int c){access(x,c);splay(x,c);while (ls[c][x]) x=ls[c][x];return x;}
void split(int x,int y,int c){makeroot(x,c);access(y,c);splay(y,c);}
void cut(int x,int y,int c){split(x,y,c);ls[c][y]=fa[c][x]=0;}
void link(int x,int y,int c){makeroot(x,c);fa[c][x]=y;}
int main()
{
N=gi();M=gi();C=gi();K=gi();
for (int i=1;i<=N;i++)
val[i]=gi();
for (int i=1,u,v,w;i<=M;i++)
{
u=gi();v=gi();w=gi();
link(u,v,w);
a[++cnt]=(edge){v,head[u],w};head[u]=cnt;
a[++cnt]=(edge){u,head[v],w};head[v]=cnt;
}
for (int i=1,opt,u,v,w,e1,e2,t1,t2;i<=K;i++)
{
opt=gi();
if (opt==0)
{
u=gi();v=gi();val[u]=v;
for (w=0;w<C;w++)
makeroot(u,w),pushup(u,w);
}
if (opt==1)
{
u=gi();v=gi();w=gi();
for (e1=head[u];e1;e1=a[e1].next)
if (a[e1].to==v) break;
for (e2=head[v];e2;e2=a[e2].next)
if (a[e2].to==u) break;
if (!e1) {puts("No such edge.");continue;}
if (a[e1].col==w) {puts("Success.");continue;}
t1=t2=0;
for (int e=head[u];e;e=a[e].next)
if (a[e].col==w) t1++;
for (int e=head[v];e;e=a[e].next)
if (a[e].col==w) t2++;
if (t1==2||t2==2) {puts("Error 1.");continue;}
if (findroot(u,w)==findroot(v,w)) {puts("Error 2.");continue;}
cut(u,v,a[e1].col);a[e1].col=a[e2].col=w;link(u,v,w);puts("Success.");
}
if (opt==2)
{
w=gi();u=gi();v=gi();
if (findroot(u,w)!=findroot(v,w)) puts("-1");
else split(u,v,w),printf("%d\n",s[w][v]);
}
}
return 0;
}
[WC2007]水管局长
一张n点m边无向图,支持两种操作:1、查询x到y的路径中边权最大值最小。2、删除边(x,y),保证边存在。
LCT维护最小生成树
删边操作在把所用询问输入之后倒序处理变成加边操作
然后注意在将边和询问排序的时候的顺序(真的是好烦啊)
#include<cstdio>
#include<algorithm>
using namespace std;
const int N = 1100005;
struct edge{int u,v,w,b,id;}e[N];
struct query{int opt,u,v,x,id;}q[N];
int n,m,Q,fa[N],ls[N],rs[N],rev[N],mx[N],val[N],Stack[N],top;
int gi()
{
int x=0,w=1;char ch=getchar();
while ((ch<'0'||ch>'9')&&ch!='-') ch=getchar();
if (ch=='-') w=0,ch=getchar();
while (ch>='0'&&ch<='9') x=(x<<3)+(x<<1)+ch-'0',ch=getchar();
return w?x:-x;
}
bool isroot(int x){return ls[fa[x]]!=x&&rs[fa[x]]!=x;}
void pushdown(int x){if (!rev[x]) return;swap(ls[x],rs[x]);rev[ls[x]]^=1;rev[rs[x]]^=1;rev[x]=0;}
void pushup(int x)//维护最大值的位置
{
mx[x]=x;
if (val[mx[x]]<val[mx[ls[x]]]) mx[x]=mx[ls[x]];
if (val[mx[x]]<val[mx[rs[x]]]) mx[x]=mx[rs[x]];
}
void R_rotate(int x)
{
int y=fa[x],z=fa[y];
ls[y]=rs[x];
if (rs[x]) fa[rs[x]]=y;
fa[x]=z;
if (!isroot(y)) if (y==ls[z]) ls[z]=x;else rs[z]=x;
rs[x]=y;fa[y]=x;
pushup(y);
}
void L_rotate(int x)
{
int y=fa[x],z=fa[y];
rs[y]=ls[x];
if (ls[x]) fa[ls[x]]=y;
fa[x]=z;
if (!isroot(y)) if (y==ls[z]) ls[z]=x;else rs[z]=x;
ls[x]=y;fa[y]=x;
pushup(y);
}
void splay(int x)
{
Stack[++top]=x;
for (int i=x;!isroot(i);i=fa[i])
Stack[++top]=fa[i];
while (top) pushdown(Stack[top--]);
while (!isroot(x))
{
int y=fa[x],z=fa[y];
if (isroot(y))
if (x==ls[y]) R_rotate(x);
else L_rotate(x);
else
if (y==ls[z])
if (x==ls[y]) R_rotate(y),R_rotate(x);
else L_rotate(x),R_rotate(x);
else
if (x==ls[y]) R_rotate(x),L_rotate(x);
else L_rotate(y),L_rotate(x);
}
pushup(x);
}
void access(int x){for (int y=0;x;y=x,x=fa[x]) splay(x),rs[x]=y,pushup(x);}
void makeroot(int x){access(x);splay(x);rev[x]^=1;}
int findroot(int x){access(x);splay(x);while (ls[x]) x=ls[x];return x;}
void split(int x,int y){makeroot(x);access(y);splay(y);}
void cut(int x,int y){split(x,y);fa[x]=ls[y]=0;}
void link(int x,int y){makeroot(x);fa[x]=y;}
bool cmp1(edge a,edge b){return a.u==b.u?a.v<b.v:a.u<b.u;}//边按u第一关键字v第二关键字排序
bool cmp2(query a,query b){return a.u==b.u?a.v<b.v:a.u<b.u;}//询问按u第一关键字v第二关键字排序
bool cmp3(edge a,edge b){return a.w<b.w;}//边按边权排序
bool cmp4(query a,query b){return a.id<b.id;}//询问按顺序
bool cmp5(edge a,edge b){return a.id<b.id;}//边按顺序
int main()
{
n=gi();m=gi();Q=gi();
for (int i=1;i<=m;i++)
{
e[i]=(edge){gi(),gi(),gi(),0,i};
if (e[i].u>e[i].v) swap(e[i].u,e[i].v);
val[i+n]=e[i].w;mx[i+n]=i+n;
}
sort(e+1,e+m+1,cmp1);
for (int i=1;i<=Q;i++)
{
q[i]=(query){gi(),gi(),gi(),0,i};
if (q[i].u>q[i].v) swap(q[i].u,q[i].v);
}
sort(q+1,q+Q+1,cmp2);
int pos=1;
for (int i=1;i<=Q;i++)
if (q[i].opt==2)
{
while (e[pos].u!=q[i].u||e[pos].v!=q[i].v) pos++;
q[i].x=e[pos].id;e[pos].b=1;
}
sort(e+1,e+m+1,cmp3);
int cnt=0;
for (int i=1;i<=m;i++)
if (e[i].b==0&&findroot(e[i].u)!=findroot(e[i].v))
{
cnt++;
link(e[i].u,e[i].id+n),link(e[i].v,e[i].id+n);//注意这里link的是e[i].id而不是i!!!
if (cnt==n-1) break;
}
sort(q+1,q+Q+1,cmp4);
sort(e+1,e+m+1,cmp5);
for (int i=Q,pos;i;i--)
{
split(q[i].u,q[i].v);pos=mx[q[i].v];
if (q[i].opt==1)
q[i].x=val[pos];
else if (e[q[i].x].w<val[pos])
{
cut(pos,e[pos-n].u);cut(pos,e[pos-n].v);
link(q[i].x+n,q[i].u);link(q[i].x+n,q[i].v);
}
}
for (int i=1;i<=Q;i++)
if (q[i].opt==1)
printf("%d\n",q[i].x);
return 0;
}
【THUWC 2017】在美妙的数学王国中畅游
数学王国里有n座城市,每座城市有三个参数\(f\),\(a\),\(b\),一个智商为\(x\)的人经过一座城市的获益\(f(x)\)是
若\(f=1\),则\(f(x)=sin(ax+b)\);
若\(f=2\),则\(f(x)=e^{ax+b}\);
若\(f=3\),则\(f(x)=ax+b\);
会发生如下四种事件:
1、有两个城市之间新建了道路;
2、有两个城市之间的道路被摧毁了;
3、城市i的三个参数被修改了;
4、求智商为x的人从u走到v的获益总和。
保证任何时候图是一个森林。
这是一道数学题
公式太难打了QWQ
总之就是维护出泰勒展开中每一项的\(\sum\)系数。精度的话算到12、13位就行了。
然而这份代码交到BZOJ上还是T
#include <cstdio>
#include <algorithm>
#include <cstring>
#include <cmath>
using namespace std;
const int N = 100005;
const int M = 13;
const double e = 2.718281828;
int n,m,fa[N],ls[N],rs[N],rev[N],f[N],Stack[N],top;
double jc[M],sum[M][N],a[N],b[N];
char type[N],s[N];
bool isroot(int x){return ls[fa[x]]!=x&&rs[fa[x]]!=x;}
void pushup(int x)
{
for (int i=0;i<M;i++)
sum[i][x]=sum[i][ls[x]]+sum[i][rs[x]];
if (f[x]==1)
{
double val=1;
for (int i=0;i<M;i++)
{
if (i%4==0) sum[i][x]+=sin(b[x])*val;
if (i%4==1) sum[i][x]+=cos(b[x])*val;
if (i%4==2) sum[i][x]+=-sin(b[x])*val;
if (i%4==3) sum[i][x]+=-cos(b[x])*val;
val*=a[x];
}
}
if (f[x]==2)
{
double val=pow(e,b[x]);sum[0][x]+=val;
for (int i=1;i<M;i++)
val*=a[x],sum[i][x]+=val;
}
if (f[x]==3)
sum[0][x]+=b[x],sum[1][x]+=a[x];
}
void pushdown(int x){if (!rev[x]) return;swap(ls[x],rs[x]);rev[ls[x]]^=1;rev[rs[x]]^=1;rev[x]=0;}
void R_rotate(int x)
{
int y=fa[x],z=fa[y];
ls[y]=rs[x];
if (rs[x]) fa[rs[x]]=y;
fa[x]=z;
if (!isroot(y)) if (y==ls[z]) ls[z]=x;else rs[z]=x;
rs[x]=y;fa[y]=x;
pushup(y);
}
void L_rotate(int x)
{
int y=fa[x],z=fa[y];
rs[y]=ls[x];
if (ls[x]) fa[ls[x]]=y;
fa[x]=z;
if (!isroot(y)) if (y==ls[z]) ls[z]=x;else rs[z]=x;
ls[x]=y;fa[y]=x;
pushup(y);
}
void splay(int x)
{
Stack[++top]=x;
for (int i=x;!isroot(i);i=fa[i])
Stack[++top]=fa[i];
while (top) pushdown(Stack[top--]);
while (!isroot(x))
{
int y=fa[x],z=fa[y];
if (isroot(y))
if (x==ls[y]) R_rotate(x);
else L_rotate(x);
else
if (y==ls[z])
if (x==ls[y]) R_rotate(y),R_rotate(x);
else L_rotate(x),R_rotate(x);
else
if (x==ls[y]) R_rotate(x),L_rotate(x);
else L_rotate(y),L_rotate(x);
}
pushup(x);
}
void access(int x){for (int y=0;x;y=x,x=fa[x]) splay(x),rs[x]=y,pushup(x);}
void makeroot(int x){access(x);splay(x);rev[x]^=1;}
int findroot(int x){access(x);splay(x);while (ls[x]) x=ls[x];return x;}
void split(int x,int y){makeroot(x);access(y);splay(y);}
void link(int x,int y){makeroot(x);fa[x]=y;}
void cut(int x,int y){split(x,y);ls[y]=fa[x]=0;}
int main()
{
freopen("math.in","r",stdin);
freopen("math.out","w",stdout);
jc[0]=1;
for (int i=1;i<M;i++)
jc[i]=jc[i-1]*i;
scanf("%d %d %s",&n,&m,type);
for (int i=1;i<=n;i++)
scanf("%d %lf %lf",&f[i],&a[i],&b[i]);
while (m--)
{
int u,v,ff;
double aa,bb,x,IQ,ans;
scanf("%s",s);
if (s[0]=='a')
{
scanf("%d %d",&u,&v);
u++;v++;
link(u,v);
}
if (s[0]=='d')
{
scanf("%d %d",&u,&v);
u++;v++;
cut(u,v);
}
if (s[0]=='m')
{
scanf("%d %d %lf %lf",&u,&ff,&aa,&bb);
u++;
makeroot(u);
f[u]=ff;a[u]=aa;b[u]=bb;
pushup(u);
}
if (s[0]=='t')
{
scanf("%d %d %lf",&u,&v,&IQ);x=1;
u++;v++;
if (findroot(u)^findroot(v)) {puts("unreachable");continue;}
split(u,v);
ans=0;
for (int i=0;i<M;i++)
ans+=sum[i][v]*x/jc[i],x*=IQ;
printf("%.8e\n",ans);
}
}
return 0;
}
原文地址:https://www.cnblogs.com/zhoushuyu/p/8137553.html