鉴于最近写bzoj还有51nod都出现写不动的现象,决定学习一波厉害的算法/数据结构。

link cut tree:研究popoqqq那个神ppt。

bzoj1036:维护access操作就可以了。

#include<cstdio>
#include<cstring>
#include<cctype>
#include<algorithm>
#include<queue>
using namespace std;
#define rep(i,s,t) for(int i=s;i<=t;i++)
#define dwn(i,s,t) for(int i=s;i>=t;i--)
#define clr(x,c) memset(x,c,sizeof(x))
#define qwq(x) for(edge *o=head[x];o;o=o->next)
int read(){
int x=0,f=1;char c=getchar();
while(!isdigit(c)){
if(c=='-') f=-1;c=getchar();
}
while(isdigit(c)) x=x*10+c-'0',c=getchar();
return x*f;
}
const int nmax=3e4+5;
const int inf=0x7f7f7f7f;
struct edge{
int to;edge *next;
};
edge es[nmax<<1],*pt=es,*head[nmax];
void add(int u,int v){
pt->to=v;pt->next=head[u];head[u]=pt++;
pt->to=u;pt->next=head[v];head[v]=pt++;
}
int n,m,fa[nmax],c[nmax][2],sm[nmax],mx[nmax],q[nmax],w[nmax];bool vis[nmax];
void bfs(){
int l=1,r=1,x;q[r]=1;vis[1]=1;
while(l<=r){
x=q[l++];
qwq(x) if(!vis[o->to]) fa[o->to]=x,q[++r]=o->to,vis[o->to]=1;
}
}
bool pdrt(int x){
return c[fa[x]][0]!=x&&c[fa[x]][1]!=x;
}
void update(int x){
int l=c[x][0],r=c[x][1];
sm[x]=sm[l]+sm[r]+w[x];mx[x]=max(w[x],max(mx[l],mx[r]));
}
void rotate(int x){
int y=fa[x],z=fa[y],l=c[y][1]==x,r=l^1;
if(!pdrt(y)) if(c[z][0]==y) c[z][0]=x;else c[z][1]=x;
fa[x]=z;fa[y]=x;fa[c[x][r]]=y;
c[y][l]=c[x][r];c[x][r]=y;
update(y);update(x);
}
void splay(int x){ //把一个点伸展到它所在的重链的根。
int y,z;//fa[x]=y表示以x为根的splay树的父亲是y,但是y的两个儿子(c[y][0]和c[y][1])中没有一个是x
while(!pdrt(x)){
y=fa[x];z=fa[y];
if(!pdrt(y)) if(c[y][0]==x^c[z][0]==y) rotate(x);else rotate(y);
rotate(x);
}
}
int access(int x){
int t=0;
while(x) splay(x),c[x][1]=t,t=x,update(x),x=fa[x];
return t;
//考虑点u,v。假设先access(u)了,那么access(lca(u,v))时lca(u,v)是原splay的根节点,
//左边的是深度比它小的点,即是lca(u,v)到根的链。 说明access的操作是正确的。
}
void qsum(int u,int v){
access(u);int lca=access(v);splay(u);
if(lca==u) printf("%d\n",w[u]+sm[c[lca][1]]);
else printf("%d\n",w[lca]+sm[c[lca][1]]+sm[u]);
}
void qmax(int u,int v){
access(u);int lca=access(v);splay(u);
if(lca==u) printf("%d\n",max(w[u],mx[c[lca][1]]));
else printf("%d\n",max(w[lca],max(mx[c[lca][1]],mx[u])));
}
int main(){
n=read();int u,v,d;mx[0]=-inf;
rep(i,1,n-1) u=read(),v=read(),add(u,v);
rep(i,1,n) sm[i]=mx[i]=w[i]=read();bfs();
m=read();char ch[11];
rep(i,1,m){
scanf("%s",ch);u=read(),v=read();
if(ch[1]=='H') splay(u),w[u]=v,update(u);
else if(ch[1]=='S') qsum(u,v);
else qmax(u,v);
}
return 0;
}

bzoj2049:link cut tree 模版题。

#include<cstdio>
#include<cstring>
#include<cctype>
#include<algorithm>
using namespace std;
#define rep(i,s,t) for(int i=s;i<=t;i++)
#define dwn(i,s,t) for(int i=s;i>=t;i--)
#define clr(x,c) memset(x,c,sizeof(x))
#define qwq(x) for(edge *o=head[x];o;o=o->next)
int read(){
int x=0,f=1;char c=getchar();
while(!isdigit(c)){
if(c=='-') f=-1;c=getchar();
}
while(isdigit(c)) x=x*10+c-'0',c=getchar();
return x*f;
}
const int nmax=1e4+5;
int n,m,fa[nmax],c[nmax][2],st[nmax],rev[nmax];
bool pdrt(int x){
return c[fa[x]][0]!=x&&c[fa[x]][1]!=x;
}
void pushdown(int x){
if(rev[x]){
int l=c[x][0],r=c[x][1];
rev[x]^=1;rev[l]^=1;rev[r]^=1;
swap(c[x][0],c[x][1]);
}
}
void rotate(int x){
int y=fa[x],z=fa[y],l=c[y][1]==x,r=l^1;
if(!pdrt(y)) if(c[z][0]==y) c[z][0]=x;else c[z][1]=x;
fa[x]=z;fa[y]=x;fa[c[x][r]]=y;
c[y][l]=c[x][r];c[x][r]=y;
}
void splay(int x){
int top=0,y,z;st[++top]=x;
for(int i=x;!pdrt(i);i=fa[i]) st[++top]=fa[i];
dwn(i,top,1) pushdown(st[i]);
while(!pdrt(x)){
y=fa[x];z=fa[y];
if(!pdrt(y)) if(c[y][0]==x^c[z][0]==y) rotate(x);else rotate(y);
rotate(x);
}
}
void access(int x){
int t=0;
while(x) splay(x),c[x][1]=t,t=x,x=fa[x];
}
void rever(int x){
access(x);splay(x);rev[x]^=1;
}
void link(int x,int y){
rever(x);fa[x]=y;
}
void cut(int x,int y){
rever(x);access(y);splay(y);c[y][0]=fa[x]=0;
}
int find(int x){
access(x);splay(x);
int y=x;while(c[y][0]) y=c[y][0];
return y;
}
int main(){
n=read(),m=read();int x,y;char ch[11];
rep(i,1,m){
scanf("%s",ch);x=read(),y=read();
if(ch[0]=='C') link(x,y);
else if(ch[0]=='D') cut(x,y);
else {
if(find(x)==find(y)) puts("Yes");
else puts("No");
}
}
return 0;
}

我这二逼智商。。。真的够了。。。。

upd9.27 重新理解了两次link cut tree 然后打起代码来顺多了。。。

bzoj2002:还是基本的操作

#include<cstdio>
#include<cstring>
#include<cctype>
#include<algorithm>
using namespace std;
#define rep(i,s,t) for(int i=s;i<=t;i++)
#define dwn(i,s,t) for(int i=s;i>=t;i--)
#define clr(x,c) memset(x,c,sizeof(x))
int read(){
int x=0;char c=getchar();
while(!isdigit(c)) c=getchar();
while(isdigit(c)) x=x*10+c-'0',c=getchar();
return x;
}
const int nmax=2e5+5;
const int inf=0x7f7f7f7f;
int fa[nmax],c[nmax][2],sz[nmax],q[nmax],nxt[nmax];
bool rev[nmax];
bool isroot(int x){
return c[fa[x]][0]!=x&&c[fa[x]][1]!=x;
}
void pushup(int x){
sz[x]=sz[c[x][0]]+sz[c[x][1]]+1;
}
void pushdown(int x){
if(rev[x]){
int l=c[x][0],r=c[x][1];
rev[x]=0;rev[l]^=1;rev[r]^=1;
swap(c[x][0],c[x][1]);
}
}
void rotate(int x){
int y=fa[x],z=fa[y],l=c[y][1]==x,r=l^1;
if(!isroot(y)) c[z][c[z][1]==y]=x;
fa[x]=z;fa[y]=x;fa[c[x][r]]=y;
c[y][l]=c[x][r];c[x][r]=y;
pushup(y);pushup(x);
}
void splay(int x){
int cnt=0,y,z;q[++cnt]=x;
for(int i=x;!isroot(i);i=fa[i]) q[++cnt]=fa[i];
dwn(i,cnt,1) pushdown(q[i]);
while(!isroot(x)){
y=fa[x],z=fa[y];
if(!isroot(y)){
if(c[y][0]==x^c[z][0]==y) rotate(x);
else rotate(y);
}
rotate(x);
}
}
void access(int x){
int t=0;
while(x) splay(x),c[x][1]=t,t=x,x=fa[x]; //这里应该是要pushup(x)的。但是为什么不加也能A啊TAT
}
void rever(int x){
access(x),splay(x),rev[x]^=1;
}
void link(int x,int y){
rever(x);fa[x]=y;
}
void cut(int x,int y){
rever(x);access(y);splay(y);c[y][0]=fa[x]=0;
}
int main(){
int n=read(),u,v,d;
rep(i,1,n){
u=read();fa[i]=min(n+1,i+u);
sz[i]=1;nxt[i]=fa[i];
}
sz[n+1]=1;
int m=read();
while(m--){
u=read();
if(u==1){
rever(n+1);v=read()+1;
access(v);splay(v);
printf("%d\n",sz[c[v][0]]);
}else{
v=read()+1;d=read();u=min(n+1,v+d);
cut(v,nxt[v]);link(v,u);nxt[v]=u;
}
}
return 0;
}
/*
4
1 2 1 1
3
1 1
2 1 1
1 1
*/

bzoj3282:还是基本操作

因为pushup我放在了x=fa[x]后面调了一节晚自修。

#include<cstdio>
#include<cstring>
#include<cctype>
#include<algorithm>
using namespace std;
#define rep(i,s,t) for(int i=s;i<=t;i++)
#define dwn(i,s,t) for(int i=s;i>=t;i--)
#define clr(x,c) memset(x,c,sizeof(x))
int read(){
int x=0;char c=getchar();
while(!isdigit(c)) c=getchar();
while(isdigit(c)) x=x*10+c-'0',c=getchar();
return x;
}
const int nmax=3e5+5;
const int inf=0x7f7f7f7f;
int fa[nmax],c[nmax][2],sz[nmax],w[nmax],q[nmax];
bool rev[nmax];
void pushup(int x){
sz[x]=w[x]^sz[c[x][0]]^sz[c[x][1]];
}
void pushdown(int x){
if(rev[x]){
int l=c[x][0],r=c[x][1];
rev[x]=0;rev[l]^=1;rev[r]^=1;
swap(c[x][0],c[x][1]);
}
}
bool isroot(int x){
return c[fa[x]][0]!=x&&c[fa[x]][1]!=x;
}
void rotate(int x){
int y=fa[x],z=fa[y],l=c[y][1]==x,r=l^1;
if(!isroot(y)) c[z][c[z][1]==y]=x;
fa[x]=z;fa[y]=x;fa[c[x][r]]=y;
c[y][l]=c[x][r];c[x][r]=y;
pushup(y);pushup(x);
}
void splay(int x){
int cnt=0,y,z;q[++cnt]=x;
for(int i=x;!isroot(i);i=fa[i]) q[++cnt]=fa[i];
dwn(i,cnt,1) pushdown(q[i]);
while(!isroot(x)){
y=fa[x];z=fa[y];
if(!isroot(y)){
if(c[y][0]==x^c[z][0]==y) rotate(x);
else rotate(y);
}
rotate(x);
}
}
void access(int x){
int t=0;
while(x) splay(x),c[x][1]=t,pushup(x),t=x,x=fa[x];
}
//因为pushup我放在了x=fa[x]后面调了一节晚自修。
void rever(int x){
access(x),splay(x),rev[x]^=1;
}
void link(int x,int y){
rever(x);fa[x]=y;
}
void cut(int x,int y){
rever(x);access(y);splay(y);c[y][0]=fa[x]=0;
}
int find(int x){
access(x);splay(x);
while(c[x][0]) x=c[x][0];
return x;
}
int main(){
int n=read(),m=read(),u,x,y;
rep(i,1,n) sz[i]=w[i]=read();
while(m--){
u=read(),x=read(),y=read();
if(u==0) {
rever(x);access(y),splay(y);
printf("%d\n",sz[y]);
}else if(u==1) {
if(find(x)!=find(y)) link(x,y);
}else if(u==2) {
if(find(x)==find(y)) cut(x,y);
}else {
access(x),splay(x),w[x]=y,pushup(x);
}
}
return 0;
}

bzoj2843&1180:还是基本操作

#include<cstdio>
#include<cstring>
#include<cctype>
#include<algorithm>
using namespace std;
#define rep(i,s,t) for(register int i=s;i<=t;i++)
#define dwn(i,s,t) for(register int i=s;i>=t;i--)
#define clr(x,c) memset(x,c,sizeof(x))
#define lc c[x][0]
#define rc c[x][1]
int read(){
int x=0;char c=getchar();
while(!isdigit(c)) c=getchar();
while(isdigit(c)) x=x*10+c-'0',c=getchar();
return x;
}
const int nmax=3e4+5;
const int inf=0x7f7f7f7f;
int fa[nmax],c[nmax][2],sm[nmax],w[nmax],q[nmax];
bool rev[nmax];
bool isroot(int x){
int t=x;x=fa[x];return lc!=t&&rc!=t;
}
void pushup(int x){
sm[x]=w[x]+sm[lc]+sm[rc];
}
void pushdown(int x){
if(rev[x]){
rev[x]=0;rev[lc]^=1;rev[rc]^=1;
swap(lc,rc);
}
}
void rotate(int x){
int y=fa[x],z=fa[y],l=c[y][1]==x,r=l^1;
if(!isroot(y)) c[z][c[z][1]==y]=x;
fa[x]=z;fa[y]=x;fa[c[x][r]]=y;
c[y][l]=c[x][r];c[x][r]=y;
pushup(y);pushup(x);
}
void splay(int x){
int cnt=0,y,z;q[++cnt]=x;
for(register int i=x;!isroot(i);i=fa[i]) q[++cnt]=fa[i];
dwn(i,cnt,1) pushdown(q[i]);
while(!isroot(x)){
y=fa[x];z=fa[y];
if(!isroot(y)) {
if(c[y][0]==x^c[z][0]==y) rotate(x);
else rotate(y);
}
rotate(x);
}
}
void access(int x){
int t=0;
while(x) splay(x),c[x][1]=t,pushup(x),t=x,x=fa[x];
}
void rever(int x){
access(x);splay(x);rev[x]^=1;
}
void link(int x,int y){
rever(x);fa[x]=y;if(!(x&233)) splay(x);//都不splayx或者都splayx会比较慢。。。
}
void cut(int x,int y){
rever(x);access(y);splay(y);c[y][0]=fa[x]=0;
}
int find(int x){
access(x);splay(x);
while(c[x][0]) x=c[x][0];
return x;
}
int main(){
int n=read();
rep(i,1,n) w[i]=sm[i]=read();
int m=read(),u,v,d;char s[15];
while(m--){
scanf("%s",s);u=read(),v=read();
if(s[0]=='b') {
if(find(u)!=find(v)) link(u,v),puts("yes");
else puts("no");
}else if(s[0]=='e'){
if(find(u)!=find(v)) puts("impossible");
else rever(u),access(v),splay(v),printf("%d\n",sm[v]);
}else{
splay(u);w[u]=v;pushup(u);
}
}
return 0;
}

bzoj3669:

3669: [Noi2014]魔法森林

Time Limit: 30 Sec  Memory Limit: 512 MB
Submit: 2156  Solved: 1310
[Submit][Status][Discuss]

Description

为了得到书法大家的真传,小E同学下定决心去拜访住在魔法森林中的隐士。魔法森林可以被看成一个包含个N节点M条边的无向图,节点标号为1..N,边标号为1..M。初始时小E同学在号节点1,隐士则住在号节点N。小E需要通过这一片魔法森林,才能够拜访到隐士。

魔法森林中居住了一些妖怪。每当有人经过一条边的时候,这条边上的妖怪就会对其发起攻击。幸运的是,在号节点住着两种守护精灵:A型守护精灵与B型守护精灵。小E可以借助它们的力量,达到自己的目的。

只要小E带上足够多的守护精灵,妖怪们就不会发起攻击了。具体来说,无向图中的每一条边Ei包含两个权值Ai与Bi。若身上携带的A型守护精灵个数不少于Ai,且B型守护精灵个数不少于Bi,这条边上的妖怪就不会对通过这条边的人发起攻击。当且仅当通过这片魔法森林的过程中没有任意一条边的妖怪向小E发起攻击,他才能成功找到隐士。

由于携带守护精灵是一件非常麻烦的事,小E想要知道,要能够成功拜访到隐士,最少需要携带守护精灵的总个数。守护精灵的总个数为A型守护精灵的个数与B型守护精灵的个数之和。

Input

第1行包含两个整数N,M,表示无向图共有N个节点,M条边。 接下来M行,第行包含4个正整数Xi,Yi,Ai,Bi,描述第i条无向边。其中Xi与Yi为该边两个端点的标号,Ai与Bi的含义如题所述。 注意数据中可能包含重边与自环。

Output

输出一行一个整数:如果小E可以成功拜访到隐士,输出小E最少需要携带的守护精灵的总个数;如果无论如何小E都无法拜访到隐士,输出“-1”(不含引号)。

WerKeyTom_FTD 神犇的题解:

我们尝试枚举路径的最大a值,那么我们只需按照a排序按顺序插入,维护1到n的b最大值即可。
用并查集维护连通性。当加入j到k这条边时如果形成环,则删除环上的最大值。
我们用动态树来进行维护。
为了实现更易,将每条边看做一个点,例如第i条边两个端点是j与k,那么将i+n与j、k相连,权值放在代表边的点上。

然后莫名其妙的空间开小了居然是TLE重新打一遍的过程中突然想到nmax开小了TAT

#include<cstdio>
#include<cstring>
#include<cctype>
#include<algorithm>
using namespace std;
#define rep(i,s,t) for(int i=s;i<=t;i++)
#define dwn(i,s,t) for(int i=s;i>=t;i--)
#define clr(x,c) memset(x,c,sizeof(x))
int read(){
int x=0;char c=getchar();
while(!isdigit(c)) c=getchar();
while(isdigit(c)) x=x*10+c-'0',c=getchar();
return x;
}
const int nmax=2e5+5;
const int maxn=1e5+5;
const int inf=0x7f7f7f7f;
int fa[nmax],c[nmax][2],mx[nmax],q[nmax],p[nmax],w[nmax];
bool rev[nmax];
struct edge{
int u,v,a,b;
bool operator<(const edge &rhs)const{
return a<rhs.a;}
};
edge es[maxn];
bool isroot(int x){
return c[fa[x]][0]!=x&&c[fa[x]][1]!=x;
}
void pushup(int x){
int l=c[x][0],r=c[x][1];
mx[x]=x;
if(w[mx[x]]<w[mx[l]]) mx[x]=mx[l];
if(w[mx[x]]<w[mx[r]]) mx[x]=mx[r];
}
void pushdown(int x){
if(rev[x]){
rev[x]=0;rev[c[x][0]]^=1;rev[c[x][1]]^=1;
swap(c[x][0],c[x][1]);
}
}
void rotate(int x){
int y=fa[x],z=fa[y],l=c[y][1]==x,r=l^1;
if(!isroot(y)) c[z][c[z][1]==y]=x;
fa[x]=z;fa[y]=x;fa[c[x][r]]=y;
c[y][l]=c[x][r];c[x][r]=y;
pushup(y);pushup(x);
}
void splay(int x){
int cnt=0,y,z;q[++cnt]=x;
for(register int i=x;!isroot(i);i=fa[i]) q[++cnt]=fa[i];
dwn(i,cnt,1) pushdown(q[i]);
while(!isroot(x)){
y=fa[x];z=fa[y];
if(!isroot(y)){
if(c[y][0]==x^c[z][0]==y) rotate(x);
else rotate(y);
}
rotate(x);
}
}
void access(int x){
int t=0;
while(x) splay(x),c[x][1]=t,pushup(x),t=x,x=fa[x];
}
void rever(int x){
access(x);splay(x);rev[x]^=1;
}
void link(int x,int y){
rever(x);fa[x]=y;if(x&233) splay(x);
}
void cut(int x,int y){
rever(x);access(y);splay(y);c[y][0]=fa[x]=0;pushup(y);
}
int find(int x){
return p[x]==x?x:p[x]=find(p[x]);
}
int query(int x,int y){
rever(x);access(y),splay(y);return mx[y];
}
void mins(int &a,int b){
if(a>b) a=b;
}
int main(){
int n=read(),m=read(),u,v,d,tmp,temp,ta,tb;
rep(i,1,m) es[i].u=read(),es[i].v=read(),es[i].a=read(),es[i].b=read();
sort(es+1,es+m+1);
int ans=inf;rep(i,1,n) p[i]=i;
rep(i,1,m){
u=es[i].u,v=es[i].v;ta=find(u),tb=find(v);
if(ta==tb){
d=query(u,v);
if(w[d]>es[i].b) cut(d,es[d-n].u),cut(d,es[d-n].v);
else {
if(find(1)==find(n)) mins(ans,es[i].a+w[query(1,n)]);
continue;
}
}else p[ta]=tb;
w[n+i]=es[i].b;mx[n+i]=n+i;
link(u,n+i),link(v,n+i);
if(find(1)==find(n)) mins(ans,es[i].a+w[query(1,n)]);
}
printf("%d\n",ans==inf?-1:ans);
return 0;
}
/*
4 5
1 2 19 1
2 3 8 12
2 4 12 15
1 3 17 8
3 4 1 17
*/

bzoj2594:

2594: [Wc2006]水管局长数据加强版

Time Limit: 25 Sec  Memory Limit: 128 MB
Submit: 2600  Solved: 840
[Submit][Status][Discuss]

Description

SC省MY市有着庞大的地下水管网络,嘟嘟是MY市的水管局长(就是管水管的啦),嘟嘟作为水管局长的工作就是:每天供水公司可能要将一定量的水从x处送往y处,嘟嘟需要为供水公司找到一条从A至B的水管的路径,接着通过信息化的控制中心通知路径上的水管进入准备送水状态,等到路径上每一条水管都准备好了,供水公司就可以开始送水了。嘟嘟一次只能处理一项送水任务,等到当前的送水任务完成了,才能处理下一项。
在处理每项送水任务之前,路径上的水管都要进行一系列的准备操作,如清洗、消毒等等。嘟嘟在控制中心一声令下,这些水管的准备操作同时开始,但由于各条管道的长度、内径不同,进行准备操作需要的时间可能不同。供水公司总是希望嘟嘟能找到这样一条送水路径,路径上的所有管道全都准备就绪所需要的时间尽量短。嘟嘟希望你能帮助他完成这样的一个选择路径的系统,以满足供水公司的要求。另外,由于MY市的水管年代久远,一些水管会不时出现故障导致不能使用,你的程序必须考虑到这一点。
不妨将MY市的水管网络看作一幅简单无向图(即没有自环或重边):水管是图中的边,水管的连接处为图中的结点。
 

Input

输入文件第一行为3个整数:N, M, Q分别表示管道连接处(结点)的数目、目前水管(无向边)的数目,以及你的程序需要处理的任务数目(包括寻找一条满足要求的路径和接受某条水管坏掉的事实)。
以下M行,每行3个整数x, y和t,描述一条对应的水管。x和y表示水管两端结点的编号,t表示准备送水所需要的时间。我们不妨为结点从1至N编号,这样所有的x和y都在范围[1, N]内。
以下Q行,每行描述一项任务。其中第一个整数为k:若k=1则后跟两个整数A和B,表示你需要为供水公司寻找一条满足要求的从A到B的水管路径;若k=2,则后跟两个整数x和y,表示直接连接x和y的水管宣布报废(保证合法,即在此之前直接连接x和y尚未报废的水管一定存在)。
 

Output

按顺序对应输入文件中每一项k=1的任务,你需要输出一个数字和一个回车/换行符。该数字表示:你寻找到的水管路径中所有管道全都完成准备工作所需要的时间(当然要求最短)。
 将狼踩尽 神犇的题解

题意:给出一个无向图,边有权值。定义一条路径的长度为该路径所有边的最大值。两种操作:(1)询问u到v所有路径的长度的最小值;(2)删掉某条边。

思路:

(1)将每个边也转化成一个点,比如边(1,2),转化成(1,3),(3,2)两个边;那么边权看做3的点权;1和2的点权可以看做0;

(2)反着做。首先将删掉的边都删掉,询问倒着回答,那么就成了加边。

(3)显然,路径长度就是最小生成树的最大值。因此,首先,将没有删掉的边求一次最小生成树,将最小生成树的边建立动态树。

(4)对于查询比较简单直接查就行。

(5)对于添加边(u,v),若u与v之前没有连通,则该边直接加进去就行,其实就是两个子树连在一起;否则,该边加进去之后会出现环,那么需要删掉环上的最大值的边。

然后因为p[x]习惯性的写成了fa[x]然后调了一节晚自修我TAT

#include<cstdio>
#include<cstring>
#include<cctype>
#include<algorithm>
using namespace std;
#define rep(i,s,t) for(int i=s;i<=t;i++)
#define dwn(i,s,t) for(int i=s;i>=t;i--)
#define clr(x,c) memset(x,c,sizeof(x))
int read(){
int x=0;char c=getchar();
while(!isdigit(c)) c=getchar();
while(isdigit(c)) x=x*10+c-'0',c=getchar();
return x;
}
const int nmax=1500005;
const int maxn=1000005;
const int inf=0x7f7f7f7f;
struct edge{
int u,v,d,id,f;
};
edge es[maxn],ns[100005];
bool cmp(edge a,edge b){
return a.d<b.d;
}
bool comp(edge a,edge b){
return a.id<b.id;
}
bool cp(edge a,edge b){
return a.u<b.u||a.u==b.u&&a.v<b.v;
}
int fa[nmax],c[nmax][2],q[nmax],mx[nmax],w[nmax],ans[nmax],p[nmax];bool rev[nmax];
bool isroot(int x){
return c[fa[x]][0]!=x&&c[fa[x]][1]!=x;
}
void pushup(int x){
int l=c[x][0],r=c[x][1];mx[x]=x;
if(w[mx[x]]<w[mx[l]]) mx[x]=mx[l];
if(w[mx[x]]<w[mx[r]]) mx[x]=mx[r];
}
void pushdown(int x){
if(rev[x]){
rev[x]=0;rev[c[x][0]]^=1;rev[c[x][1]]^=1;
swap(c[x][0],c[x][1]);
}
}
void rotate(int x){
int y=fa[x],z=fa[y],l=c[y][1]==x,r=l^1;
if(!isroot(y)) c[z][c[z][1]==y]=x;
fa[x]=z;fa[y]=x;fa[c[x][r]]=y;
c[y][l]=c[x][r];c[x][r]=y;
pushup(y);pushup(x);
}
void splay(int x){
int cnt=0,y,z;q[++cnt]=x;
for(int i=x;!isroot(i);i=fa[i]) q[++cnt]=fa[i];
dwn(i,cnt,1) pushdown(q[i]);
while(!isroot(x)){
y=fa[x];z=fa[y];
if(!isroot(y)){
if(c[y][0]==x^c[z][0]==y) rotate(x);
else rotate(y);
}
rotate(x);
}
}
void access(int x){
int t=0;
while(x) splay(x),c[x][1]=t,pushup(x),t=x,x=fa[x];
}
void rever(int x){
access(x);splay(x);rev[x]^=1;
}
void link(int x,int y){
rever(x);fa[x]=y;
}
void cut(int x,int y){
rever(x);access(y);splay(y);c[y][0]=fa[x]=0;
}
int query(int x,int y){
rever(x);access(y);splay(y);return mx[y];
}
int find(int x,int y,int m){
int l=1,r=m,mid;
while(l<=r){
mid=(l+r)>>1;
if(es[mid].u<x||es[mid].u==x&&es[mid].v<y) l=mid+1;
else if(es[mid].u==x&&es[mid].v==y) return mid;
else r=mid-1;
}
}
int ff(int x){
return p[x]==x?x:p[x]=ff(p[x]);
}
int main(){
int n=read(),m=read(),q=read(),tmp,temp,u,v,d,ta,tb;
rep(i,1,m) {
es[i].u=read(),es[i].v=read(),es[i].d=read();
if(es[i].u>es[i].v) swap(es[i].u,es[i].v);
}
sort(es+1,es+m+1,cmp);
rep(i,1,m) es[i].id=i,w[n+i]=es[i].d,mx[n+i]=n+i;
sort(es+1,es+m+1,cp);
rep(i,1,q) {
ns[i].d=read(),ns[i].u=read(),ns[i].v=read();
if(ns[i].u>ns[i].v) swap(ns[i].u,ns[i].v);
if(ns[i].d==2) tmp=find(ns[i].u,ns[i].v,m),es[tmp].f=1,ns[i].id=es[tmp].id;
}
sort(es+1,es+m+1,comp);
int cnt=0;rep(i,1,n) p[i]=i;
rep(i,1,m) if(!es[i].f){
u=es[i].u;v=es[i].v;ta=ff(u);tb=ff(v);
if(ta!=tb){
p[ta]=tb;link(u,i+n);link(v,i+n);//因为这里的p习惯性的打成了fa调了一节晚自修各种中间输出就是在这里WA然后一直检查模版TAT
if(++cnt==n-1) break;
}
}
dwn(i,q,1){
if(ns[i].d==1) ans[i]=w[query(ns[i].u,ns[i].v)];
else{
u=ns[i].u,v=ns[i].v,d=ns[i].id;tmp=query(u,v);
if(es[d].d<w[tmp]) {
cut(tmp,es[tmp-n].u);cut(tmp,es[tmp-n].v);
link(u,d+n);link(v,d+n);
}
}
}
rep(i,1,q) if(ns[i].d==1) printf("%d\n",ans[i]);
return 0;
}

  

link cut tree 入门的更多相关文章

  1. bzoj2049 [Sdoi2008]Cave 洞穴勘测 link cut tree入门

    link cut tree入门题 首先说明本人只会写自底向上的数组版(都说了不写指针.不写自顶向下QAQ……) 突然发现link cut tree不难写... 说一下各个函数作用: bool isro ...

  2. Codeforces Round #339 (Div. 2) A. Link/Cut Tree 水题

    A. Link/Cut Tree 题目连接: http://www.codeforces.com/contest/614/problem/A Description Programmer Rostis ...

  3. Link/cut Tree

    Link/cut Tree 一棵link/cut tree是一种用以表示一个森林,一个有根树集合的数据结构.它提供以下操作: 向森林中加入一棵只有一个点的树. 将一个点及其子树从其所在的树上断开. 将 ...

  4. 洛谷P3690 Link Cut Tree (模板)

    Link Cut Tree 刚开始写了个指针版..调了一天然后放弃了.. 最后还是学了黄学长的板子!! #include <bits/stdc++.h> #define INF 0x3f3 ...

  5. LCT总结——概念篇+洛谷P3690[模板]Link Cut Tree(动态树)(LCT,Splay)

    为了优化体验(其实是强迫症),蒟蒻把总结拆成了两篇,方便不同学习阶段的Dalao们切换. LCT总结--应用篇戳这里 概念.性质简述 首先介绍一下链剖分的概念(感谢laofu的讲课) 链剖分,是指一类 ...

  6. P3690 【模板】Link Cut Tree (动态树)

    P3690 [模板]Link Cut Tree (动态树) 认父不认子的lct 注意:不 要 把 $fa[x]$和$nrt(x)$ 混 在 一 起 ! #include<cstdio> v ...

  7. Link Cut Tree学习笔记

    从这里开始 动态树问题和Link Cut Tree 一些定义 access操作 换根操作 link和cut操作 时间复杂度证明 Link Cut Tree维护链上信息 Link Cut Tree维护子 ...

  8. [CodeForces - 614A] A - Link/Cut Tree

    A - Link/Cut Tree Programmer Rostislav got seriously interested in the Link/Cut Tree data structure, ...

  9. Link Cut Tree 总结

    Link-Cut-Tree Tags:数据结构 ##更好阅读体验:https://www.zybuluo.com/xzyxzy/note/1027479 一.概述 \(LCT\),动态树的一种,又可以 ...

随机推荐

  1. 7件你不知道但可以用CSS做的事

    不管你信不信,CSS和JavaScript开始重叠,就像CSS增加了更多功能一新.在我写“你可能不知道的CSS和JavaScript互相影响的5种方式”一文时,人们对于JavaScript和CSS是如 ...

  2. Java检查型异常和非检查型异常

    1.代码 public class ExcepTest { /** * @param args */ public static void main(String[] args) { System.e ...

  3. MySQL注入load_file常用路径

    WINDOWS下: c:/boot.ini //查看系统版本 c:/windows/php.ini //php配置信息 c:/windows/my.ini //MYSQL配置文件,记录管理员登陆过的M ...

  4. D3D depth buffer的预览

    在使用D3D开发游戏的过程中,很多情况下都会用到depth buffer来完成特定的效果,比如DOF,Shadows,SSAO等等.在这些情况下我们就可能需要预览depth buffer来确定它是正确 ...

  5. MySQL数据库数据类型之集合类型SET测试总结

    MySQL数据库提供针对字符串存储的一种特殊数据类型:集合类型SET,这种数据类型可以给予我们更多提高性能.降低存储容量和降低程序代码理解的技巧,前面介绍了首先介绍了四种数据类型的特性总结,其后又分别 ...

  6. Log4Net 全方位跟踪程序运行

    原文:http://kb.cnblogs.com/page/100968/ 1.Log4Net 简单使用 使用Log4net必须引入log4net.dll 文件,因为log4net 是开源的,所以可以 ...

  7. FastDfs点滴

    1.centos安装后提示找不到libevent动态库 根据系统是64位版本还是32位版本,若是64位版本则默认回到 /usr/lib64 目录下查找,而对于32位则到 /usr/lib 目录下查找. ...

  8. poj 2723

    Get Luffy Out Time Limit: 2000MS   Memory Limit: 65536K Total Submissions: 7295   Accepted: 2778 Des ...

  9. Freebie: Material Design UI Kit

    点这里 Following the guidelines laid out by Google, this free UI kit has been designed so that you can ...

  10. 百度面试题——top K算法

    需求 从一亿个数据中,找出其中最小的10个数. 分析 最笨的方法就是将这一亿个数据,按从小到大进行排序,然后取前10个.这样的话,即使使用时间复杂度为nlogn的快排或堆排,由于元素会频繁的移动,效率 ...