题目大意:
某校开展了同学们喜闻乐见的阳光长跑活动。为了能“为祖国健康工作五十年”,同学们纷纷离开寝室,离开教室,离开实验室,到操场参加3000米长跑运动。一时间操场上熙熙攘攘,摩肩接踵,盛况空前。
为了让同学们更好地监督自己,学校推行了刷卡机制。
学校中有n个地点,用1到n的整数表示,每个地点设有若干个刷卡机。
有以下三类事件:
1、修建了一条连接A地点和B地点的跑道。
2、A点的刷卡机台数变为了B。
3、进行了一次长跑。问一个同学从A出发,最后到达B最多可以刷卡多少次。具体的要求如下:
当同学到达一个地点时,他可以在这里的每一台刷卡机上都刷卡。但每台刷卡机只能刷卡一次,即使多次到达同一地点也不能多次刷卡。
为了安全起见,每条跑道都需要设定一个方向,这条跑道只能按照这个方向单向通行。最多的刷卡次数即为在任意设定跑道方向,按照任意路径从A地点到B地点能刷卡的最多次数。
思路:
考虑怎么样才会取到一条路径上面的最大值,不难发现当路径中有环的时候这个环的所有点都可以收割一遍。其实就是把路径中的所有的边双算在里面,也就是把边双缩成一个点之后和边双之内的其他点连边并且权值为边双内的所有点权值和。
考虑在带修改的情况下怎么动态维护边双。其实每一次加进去发现有环就可以缩成一个点,这样刚好也达到了缩边双的效果。这样说来就好像可以用lct来维护这个图的形态,如果有新的环出现,就把这环上的这条路径全部取出来缩成一个点。但是好像直接缩点很不可做的样子,因为我们还要改变所有和这条链有连边的splay的父亲,而我们根本就找不到这些splay究竟在哪里。一个办法是用并查集将这条链上所有的除了顶点之外的点都合并到顶点那个集合,这样如果有splay是连向这条链上的点,我们在找splay父亲的时候就找它父亲所在的并查集的集合也就是那个顶点,结果也就是相当于把这个splay连到了顶点上面,而顶点所代表的就是这条链缩成的边双。
注意每一次连接操作都要把点先设为所在的边双的顶点的编号,因为有可能是合并边双。
同时还要开数组存在每一个点目前的权值。
最后记得卡一卡常。
#include<bits/stdc++.h>
#define REP(i,a,b) for(int i=a;i<=b;++i)
typedef long long ll;
using namespace std;
void File(){
freopen("bzoj2959.in","r",stdin);
freopen("bzoj2959.out","w",stdout);
}
template<typename T>
void read(T &x){
T _=0,mul=1;
char __=getchar();
while(!isdigit(__)){
if(__=='-')mul=-1;
__=getchar();
}
while(isdigit(__))_=(_<<1)+(_<<3)+(__^'0'),__=getchar();
x=_*mul;
}
template<typename T>
void write(T x,char c){
if(x==0){
putchar('0');
putchar(c);
return;
}
if(x<0){putchar('-');x=-x;}
T len=1,y=10;
while(y<=x)y=(y<<3)+(y<<1),++len;
while(len--){
y/=10;
putchar(x/y+48);
x%=y;
}
putchar(c);
}
const int maxn=15e4+10;
int n,m,f1[maxn],f2[maxn];
int finds(int x,int *f){return f[x]==x ? x : f[x]=finds(f[x],f);}
struct lct{
#define lc ch[rt][0]
#define rc ch[rt][1]
#define getf(x) (finds(fa[x],f1))
#define rel(x) (ch[getf(x)][1]==x)
#define isrt(x) (ch[getf(x)][0]!=x && ch[getf(x)][1]!=x)
int fa[maxn],ch[maxn][2],val[maxn],sum[maxn],qu[maxn],tp,nowval[maxn];
bool tag[maxn];
void pushup(int rt){sum[rt]=val[rt]+sum[lc]+sum[rc];}
void pushdown(int rt){if(tag[rt])swap(lc,rc),tag[lc]^=1,tag[rc]^=1,tag[rt]=0;}
void rotate(int rt){
int f=getf(rt),r=rel(rt);
fa[rt]=getf(f); if(!isrt(f))ch[getf(f)][rel(f)]=rt;
fa[ch[rt][r^1]]=f; ch[f][r]=ch[rt][r^1];
fa[f]=rt; ch[rt][r^1]=f;
pushup(f); pushup(rt);
}
void splay(int rt){
int p=rt;
for(tp=0;;p=getf(p)){
qu[++tp]=p;
if(isrt(p))break;
}
while(tp)pushdown(qu[tp--]);
for(int f=getf(rt);!isrt(rt);rotate(rt),f=getf(rt))
if(!isrt(f))rotate(rel(f)==rel(rt) ? f : rt);
}
void access(int rt){for(int res=0;rt;rc=res,pushup(rt),res=rt,rt=getf(rt))splay(rt);}
void makert(int rt){access(rt);splay(rt);tag[rt]^=1;}
void split(int x,int y){makert(x);access(y);splay(y);}
int findrt(int rt){access(rt);splay(rt);while(lc)rt=lc;return rt;}
bool con(int x,int y){return finds(x,f2)==finds(y,f2);}
void link(int x,int y){if(!con(x,y))makert(x),fa[x]=y;}
void cut(int x,int y){split(x,y);if(ch[y][0]==x && !ch[x][1])ch[y][0]=fa[x]=0;}
void update(int x,int y){
splay(finds(x,f1));
val[finds(x,f1)]+=y-nowval[x];
nowval[x]=y;pushup(finds(x,f1));
}
int query(int x,int y){split(x,y);return sum[y];}
void dfs(int u,int rt){
if(u!=rt)val[rt]+=val[u],f1[finds(u,f1)]=finds(rt,f1);
if(ch[u][0])dfs(ch[u][0],rt);
if(ch[u][1])dfs(ch[u][1],rt);
ch[u][0]=ch[u][1]=0;
}
}T;
int main(){
File();
read(n); read(m);
REP(i,1,n)f1[i]=f2[i]=i;
REP(i,1,n){
read(T.val[i]);
T.sum[i]=T.nowval[i]=T.val[i];
}
REP(i,1,m){
int ty,x,y;
read(ty); read(x); read(y);
if(ty==1){
x=finds(x,f1); y=finds(y,f1);
if(T.con(x,y))T.split(x,y),T.dfs(y,y);
else{
T.link(x,y);
f2[finds(x,f2)]=finds(y,f2);
}
}
else if(ty==2)T.update(x,y);
else{
x=finds(x,f1); y=finds(y,f1);
if(!T.con(x,y))puts("-1");
else write(T.query(x,y),'\n');
}
}
return 0;
}