题目链接:
题目大意:有$n$个点$m$次操作,每次操作分为三种:1、在$u,v$两点之间连接一条编号为$id$,长度为$l$,温度为$t$的边。2、查询从$u$到$v$的最温暖的路径长度(定义最温暖的路径为将路径上的边按温度从小到大排序后字典序尽可能大)。3、将编号为$id$的边长度修改为$l$。
仔细读题发现题目中说的字典序其实就是使路径上的边都尽可能大。
那么最优方案一定就是走最大生成树上的边咯。
用LCT动态维护最大生成树。
当新加入边两端点不连通时直接连接,否则找到两点路径上的最小边,如果当前边比最小边大,那么将最小边删除并加入当前边。
在LCT上维护边的信息不好做,我们将每个边看成一个点。
例如编号为a的边连接x,y,那么就将a+n这个新建点分别连向x,y。
splay需要维护最小点权,点权和及最小点权点编号。
注意要将所有非边点的点权置成INF。
注意在find之后要将原树根旋到它所在splay的根,否则在uoj会被卡。
#include<set> #include<map> #include<queue> #include<stack> #include<cmath> #include<vector> #include<bitset> #include<cstdio> #include<cstring> #include<iostream> #include<algorithm> #define pr pair<int,int> #define ll long long using namespace std; char ch[10]; int n,m; int x,y,z,u,v; struct miku { int x,y; }a[600010]; int s[800010][2]; int mx[800010]; int mn[800010]; int r[800010]; int f[800010]; int w[800010]; int st[800010]; int sum[800010]; int t[800010]; int is_root(int rt) { return rt!=s[f[rt]][0]&&rt!=s[f[rt]][1]; } int get(int rt) { return rt==s[f[rt]][1]; } void pushup(int rt) { sum[rt]=sum[s[rt][0]]+sum[s[rt][1]]+w[rt]; mx[rt]=min(t[rt],min(mx[s[rt][0]],mx[s[rt][1]])); if(mx[rt]==t[rt]) { mn[rt]=rt; } else if(mx[rt]==mx[s[rt][0]]) { mn[rt]=mn[s[rt][0]]; } else { mn[rt]=mn[s[rt][1]]; } } void pushdown(int rt) { if(r[rt]) { swap(s[rt][0],s[rt][1]); r[s[rt][0]]^=1; r[s[rt][1]]^=1; r[rt]^=1; } } void rotate(int rt) { int fa=f[rt]; int anc=f[fa]; int k=get(rt); if(!is_root(fa)) { s[anc][get(fa)]=rt; } s[fa][k]=s[rt][k^1]; f[s[fa][k]]=fa; s[rt][k^1]=fa; f[fa]=rt; f[rt]=anc; pushup(fa); pushup(rt); } void splay(int rt) { int top=0; st[++top]=rt; for(int i=rt;!is_root(i);i=f[i]) { st[++top]=f[i]; } for(int i=top;i>=1;i--) { pushdown(st[i]); } for(int fa;!is_root(rt);rotate(rt)) { if(!is_root(fa=f[rt])) { rotate(get(fa)==get(rt)?fa:rt); } } } void access(int rt) { for(int x=0;rt;x=rt,rt=f[rt]) { splay(rt); s[rt][1]=x; pushup(rt); } } void reverse(int rt) { access(rt); splay(rt); r[rt]^=1; } int find(int rt) { access(rt); splay(rt); while(s[rt][0]) { rt=s[rt][0]; } splay(rt); return rt; } void link(int x,int y) { reverse(x); f[x]=y; } void cut(int x,int y) { reverse(x); access(y); splay(y); if(s[x][1]||f[x]!=y) { return ; } f[x]=s[y][0]=0; pushup(y); } void change(int rt,int x) { w[rt]=x; access(rt); splay(rt); } int query(int x,int y) { reverse(x); access(y); splay(y); return mn[y]; } int ask(int x,int y) { reverse(x); access(y); splay(y); return sum[y]; } int main() { scanf("%d%d",&n,&m); for(int i=0;i<=n;i++) { t[i]=1000000007; mx[i]=1000000007; mn[i]=i; w[i]=0; sum[i]=0; } while(m--) { scanf("%s",ch); if(ch[0]=='f') { scanf("%d%d%d%d%d",&x,&u,&v,&y,&z); u++; v++; x++; a[x].x=u; a[x].y=v; t[n+x]=y; w[n+x]=z; sum[n+x]=z; mn[n+x]=n+x; if(find(u)==find(v)) { int id=query(u,v); if(y>mx[id]) { cut(id,a[id-n].x); cut(id,a[id-n].y); link(n+x,u); link(n+x,v); } } else { link(n+x,u); link(n+x,v); } } else if(ch[0]=='c') { scanf("%d%d",&x,&y); x++; change(n+x,y); } else { scanf("%d%d",&u,&v); u++; v++; if(find(u)==find(v)) { printf("%d ",ask(u,v)); } else { printf("-1 "); } } } }