http://www.elijahqi.win/2018/02/15/bzoj2959/
Description
某校开展了同学们喜闻乐见的阳光长跑活动。为了能“为祖国健康工作五十年”,同学们纷纷离开寝室,离开教室,离开实验室,到操场参加3000米长跑运动。一时间操场上熙熙攘攘,摩肩接踵,盛况空前。
为了让同学们更好地监督自己,学校推行了刷卡机制。
学校中有n个地点,用1到n的整数表示,每个地点设有若干个刷卡机。
有以下三类事件:
1、修建了一条连接A地点和B地点的跑道。
2、A点的刷卡机台数变为了B。
3、进行了一次长跑。问一个同学从A出发,最后到达B最多可以刷卡多少次。具体的要求如下:
当同学到达一个地点时,他可以在这里的每一台刷卡机上都刷卡。但每台刷卡机只能刷卡一次,即使多次到达同一地点也不能多次刷卡。
为了安全起见,每条跑道都需要设定一个方向,这条跑道只能按照这个方向单向通行。最多的刷卡次数即为在任意设定跑道方向,按照任意路径从A地点到B地点能刷卡的最多次数。
Input
输入的第一行包含两个正整数n,m,表示地点的个数和操作的个数。
第二行包含n个非负整数,其中第i个数为第个地点最开始刷卡机的台数。
接下来有m行,每行包含三个非负整数P,A,B,P为事件类型,A,B为事件的两个参数。
最初所有地点之间都没有跑道。
每行相邻的两个数之间均用一个空格隔开。表示地点编号的数均在1到n之间,每个地点的刷卡机台数始终不超过10000,P=1,2,3。
Output
输出的行数等于第3类事件的个数,每行表示一个第3类事件。如果该情况下存在一种设定跑道方向的方案和路径的方案,可以到达,则输出最多可以刷卡的次数。如果A不能到达B,则输出-1。
Sample Input
9 31
10 20 30 40 50 60 70 80 90
3 1 2
1 1 3
1 1 2
1 8 9
1 2 4
1 2 5
1 4 6
1 4 7
3 1 8
3 8 8
1 8 9
3 8 8
3 7 5
3 7 3
1 4 1
3 7 5
3 7 3
1 5 7
3 6 5
3 3 6
1 2 4
1 5 5
3 3 6
2 8 180
3 8 8
2 9 190
3 9 9
2 5 150
3 3 6
2 1 210
3 3 6
Sample Output
-1
-1
80
170
180
170
190
170
250
280
280
270
370
380
580
HINT
数据规模及约定
对于100%的数据,m<=5n,任意时刻,每个地点的刷卡机台数不超过10000。N<=1.5×105
Source
中国国家队清华集训 2012-2013 第二天
和上一题很像几乎同理 注意修改的时候需要先splay到根然后再改 我zz了 要不然 整棵树的信息没有办法维护了
因为只有加边操作可以考虑用并查集维护 fa1 存连通性 fa2存是否属于同一个点 那么我每次加边的时候首先判断下当前这两个点是否在同一个连通块内 如果在 直接返回答案即可 如果不在则判断下这两个是否已经连通 没有连通的话就用lct link一下 如果已经连通了 那么说明经过我这次操作可以让我变成边双 那么每当这时我就取dfs一下统计答案即可 同时把并查集 的fa2都指向我当前这个集合的头即可 注意因为我要删点 所以在做任何操作的时候我都需要用并查集 找到我这个集合的头 然后往上跳才可以 具体地,对于每个加边操作,如果它们不在同一个边双里且未连通,则把它们所在边双连上。否则如果它们不在同一个边双里且已经连通,则需要提取它们之间的路径,把路径上的点所在边双全部改为新的边双并在LCT中“删除”这些点。这个过程可以直接对Splay Tree进行dfs实现,并使用并查集来维护。
#include<cstdio>
#include<algorithm>
#define N 165000
using namespace std;
inline char gc(){
static char now[1<<16],*S,*T;
if (T==S) {T=(S=now)+fread(now,1,1<<16,stdin);if (T==S) return EOF;}
return *S++;
}
inline int read(){
int x=0,f=1;char ch=gc();
while(ch<'0'||ch>'9') {if (ch=='-') f=-1;ch=gc();}
while(ch<='9'&&ch>='0') x=x*10+ch-'0',ch=gc();
return x*f;
}
int fa1[N],fa2[N],v[N],sum[N],fa[N],n,m,s[N],c[N][2],rev[N],top,q[N];
inline void update(int x){
sum[x]=sum[c[x][0]]+sum[c[x][1]]+v[x];
}
inline void pushdown(int x){
if (!rev[x]) return;rev[x]=0;
int l=c[x][0],r=c[x][1];
swap(c[x][0],c[x][1]);rev[l]^=1;rev[r]^=1;
}
inline int find1(int x){
while(x!=fa1[x]) x=fa1[x]=fa1[fa1[x]];return x;
}
inline int find2(int x){
while(x!=fa2[x]) x=fa2[x]=fa2[fa2[x]];return x;
}
inline bool isroot(int x){
return c[find2(fa[x])][0]!=x&&c[find2(fa[x])][1]!=x;
}
inline void rotate(int x){
int y=find2(fa[x]),z=find2(fa[y]);
if (!isroot(y)) c[z][c[z][1]==y]=x;
int l=c[y][1]==x,r=l^1;
fa[c[x][r]]=y;fa[y]=x;fa[x]=z;
c[y][l]=c[x][r];c[x][r]=y;update(y);update(x);
}
inline void splay(int x){
q[top=1]=x;for (int i=x;!isroot(i);i=find2(fa[i])) q[++top]=fa[i];
while(top) pushdown(q[top--]);
while(!isroot(x)){
int y=find2(fa[x]),z=find2(fa[y]);
if (!isroot(y)){
if (c[y][0]==x^c[z][0]==y) rotate(x);else rotate(y);
}rotate(x);
}
}
inline void access(int x){
for (int t=0;x;t=x,x=find2(fa[x])) splay(x),c[x][1]=t,update(x);
}
inline void makeroot(int x){
access(x);splay(x);rev[x]^=1;
}
inline void link(int x,int y){
makeroot(x);fa1[find1(x)]=find1(y);fa[x]=y;
}
inline void dfs(int x,int f){
if (!x) return;fa2[x]=f;dfs(c[x][0],f);dfs(c[x][1],f);
}
inline void add(int x,int y){
x=find2(x);y=find2(y);
if (x==y) return;
if (find1(x)!=find1(y)) {link(x,y);return;}
makeroot(x);access(y);splay(y);v[y]=sum[y];dfs(c[y][0],y);
}
int main(){
//freopen("bzoj2959.in","r",stdin);
n=read();m=read();
for (int i=1;i<=n;++i) s[i]=read(),v[i]=s[i],fa1[i]=i,fa2[i]=i;
while(m--){
int op=read(),x=read(),y=read();
if (op==1) add(x,y);
if (op==2){
int tmp=s[x];s[x]=y;
x=find2(x);splay(x);v[x]+=y-tmp;sum[x]+=y-tmp;
}
if (op==3){
if (find1(x)!=find1(y)) {puts("-1");continue;}
x=find2(x);y=find2(y);
makeroot(x);access(y);splay(y);printf("%d\n",sum[y]);
}
}
return 0;
}