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
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
int findrt(int x) { access(x);splay(x);while(ch[x][0]) x=ch[x][0]; return x; }
乍一看,你会说因为splay均摊O(logn),所以这个操作也均摊O(logn)。
但,它是O(n)的。
原理不难理解这里就不赘述了。
那么对于这道题我们发现第三问就是对原图缩圈后两点间路径的权值,我们可以考虑用动态树来动态维护缩圈。
原理很简单,只要在加入的新边成环时将整个环上的点重新合并一下,因为每个点最多被合并一次,所以时间复杂度仍未O(MlogN)。
#include<cstdio> #include<cctype> #include<queue> #include<cstring> #include<algorithm> #define lc ch[x][0] #define rc ch[x][1] #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 ren for(int i=first[x];i;i=next[i]) using namespace std; const int BufferSize=1<<16; char buffer[BufferSize],*head,*tail; inline char Getchar() { if(head==tail) { int l=fread(buffer,1,BufferSize,stdin); tail=(head=buffer)+l; } return *head++; } inline int read() { int x=0,f=1;char c=Getchar(); for(;!isdigit(c);c=Getchar()) if(c=='-') f=-1; for(;isdigit(c);c=Getchar()) x=x*10+c-'0'; return x*f; } const int maxn=150010; int n,m,pa[maxn],pa2[maxn],del[maxn],A[maxn]; int findset(int x) {return pa[x]==x?x:pa[x]=findset(pa[x]);} int findrt(int x) {return pa2[x]==x?x:pa2[x]=findrt(pa2[x]);} int flip[maxn],fa[maxn],pre[maxn],ch[maxn][2],s[maxn],val[maxn],sumv[maxn]; void maintain(int x) { s[x]=s[lc]+s[rc]+1; sumv[x]=sumv[lc]+sumv[rc]+val[x]; } void pushdown(int x) { if(flip[x]) { swap(lc,rc); flip[lc]^=1;flip[rc]^=1; flip[x]=0; } } void rotate(int x) { int y=pre[x],z=pre[y],d=ch[y][0]==x; ch[y][d^1]=ch[x][d];pre[ch[x][d]]=y; ch[z][ch[z][1]==y]=x;pre[x]=z; ch[x][d]=y;pre[y]=x;maintain(y); } int S[maxn],top; void splay(int x) { for(int i=x;i;i=pre[i]) S[++top]=i; if(top!=1) fa[x]=fa[S[top]],fa[S[top]]=0; while(top) pushdown(S[top--]); while(pre[x]) rotate(x); maintain(x); } void access(int x) { for(int y=0;x;x=findset(fa[x])) { splay(x);pre[ch[x][1]]=0;fa[ch[x][1]]=x; ch[x][1]=y;pre[y]=x;maintain(y=x); } } void makeroot(int x) {access(x);splay(x);flip[x]^=1;} void link(int x,int y) {makeroot(x);fa[x]=y;} void query(int x,int y) { makeroot(x);access(y);splay(y); printf("%d\n",sumv[y]); } void dfs(int x,int y) { if(!x) return;pushdown(x); pa[findset(x)]=findset(y); if(x!=y) val[y]+=val[x]; dfs(ch[x][0],y);dfs(ch[x][1],y); ch[x][0]=ch[x][1]=0; } void cycle(int x,int y) { makeroot(x);access(y);splay(y);dfs(y,y); maintain(y); } int main() { n=read();m=read(); rep(i,1,n) pa[i]=pa2[i]=i,val[i]=A[i]=read(); rep(i,1,m) { int t=read(),x=read(),y=read(); if(t==1) { x=findset(x);y=findset(y);if(x==y) continue; if(findrt(x)!=findrt(y)) link(x,y),pa2[findrt(x)]=findrt(y); else cycle(x,y); } else if(t==2) { splay(findset(x)); val[findset(x)]+=y-A[x];A[x]=y; maintain(findset(x)); } else { x=findset(x);y=findset(y); if(findrt(x)!=findrt(y)) puts("-1"); else query(x,y); } } return 0; }