P3206 [HNOI2010]城市建设 分治,MST/线段树分治,LCT
每个时刻修改图上的一条边的边权(永久修改) 求每个时刻的最小生成树。
容易想到线段树分治,这样就没有了删除操作只有加边操作和回到上一个状态的操作。
回到上一个状态的操作可以多开一个数组来记录他。
那么加边操作其实就是一个动态的最小生成树问题利用LCT即可。
虽然被秒了但是码量很大,LCT狗都不写。
考虑利用分治思想 不过边数和点数到达最后一层级别都是O(n) 和 O(m)的
有一个trick 把当前分治区间内的边都变为正无穷那么 再做最小生成树 那么没有加入的后续也不会加入。边的数量限制到了区间大小级别。
变为负无穷 再做最小生成树 那么现在加入的将来一定也加入 缩点即可。点的数量变成了区间大小级别。
这样分治的复杂度就对了。这个分治姑且有一点点CDQ分治的思想 算作CDQ分治类的吧。
code
//#include<bits/stdc++.h>
#include<iostream>
#include<cstdio>
#include<ctime>
#include<cctype>
#include<queue>
#include<deque>
#include<stack>
#include<iostream>
#include<iomanip>
#include<cstdio>
#include<cstring>
#include<string>
#include<ctime>
#include<cmath>
#include<cctype>
#include<cstdlib>
#include<queue>
#include<deque>
#include<stack>
#include<vector>
#include<algorithm>
#include<utility>
#include<bitset>
#include<set>
#include<map>
#define ll long long
#define db double
#define INF 2000000000
#define inf 100000000000000000ll
#define ldb long double
#define pb push_back
#define put_(x) printf("%d ",x);
#define get(x) x=read()
#define putl(x) printf("%lld\n",x)
#define rep(p,n,i) for(int i=p;i<=n;++i)
#define go(x) for(int i=lin[x],tn=ver[i];i;tn=ver[i=nex[i]])
#define pii pair<int,int>
#define mk make_pair
#define P 1000000007ll
#define gf(x) scanf("%lf",&x)
#define pf(x) ((x)*(x))
#define uint unsigned long long
#define ui unsigned
#define sq sqrt
#define y(w) t[w].y
#define x(w) t[w].x
#define z(w) t[w].z
#define id(cc) s[cc].id
#define w(cc) s[cc].w
#define S second
#define mod 1000000007
#define sc(A) scanf("%d",&A)
#define scs(A) scanf("%s",A);
#define put(A) printf("%d\n",A)
#define min(x,y) (x>=y?y:x)
#define max(x,y) (x>=y?x:y)
using namespace std;
const int MAXN=50010;
int n,m,q;
struct wy{int x,y,z;}t[MAXN];
struct jl{int id,w;}s[MAXN];
int F[20][MAXN],SZ[20][MAXN],A[20][MAXN],*a,*f,*sz;
int B[20][MAXN],W[20][MAXN],W2[20][MAXN],W1[20][MAXN],vis[MAXN],*b,*w;
//f数组和sz数组需要回退.
inline int cmp(int x,int y){return z(x)<z(y);}
inline int getfather(int x){return x==f[x]?x:getfather(f[x]);}
inline int merge(int x,int y)
{
int xx=getfather(x);
int yy=getfather(y);
if(xx==yy)return 0;
if(sz[yy]>sz[xx])swap(xx,yy);
f[yy]=xx;sz[xx]+=sz[yy];
return 1;
}
inline void CDQ(int d,int l,int r,int n,int m,ll ans)
{
//到达每一层先对l~r的边全连上再跑MST判断该连的连上
rep(l,r,i)vis[id(i)]=1;
a=A[d-1];
rep(1,n,i)F[d][a[i]]=F[d-1][a[i]],SZ[d][a[i]]=SZ[d-1][a[i]];
rep(1,m,i)B[d][i]=B[d-1][i];
f=F[d];sz=SZ[d];b=B[d];
sort(b+1,b+1+m,cmp);
//正无穷
rep(1,m,i)
{
if(!vis[b[i]])
{
if(merge(x(b[i]),y(b[i])))
{
//cout<<b[i]<<endl;
b[i]=-b[i];
}
}
}
rep(1,n,i)f[a[i]]=F[d-1][a[i]],sz[a[i]]=SZ[d-1][a[i]];
int m1=0;
rep(1,m,i)if(b[i]<0||vis[b[i]]){b[++m1]=abs(b[i]);}
//负无穷
//if(l==3)
// cout<<"ww"<<endl;
rep(l,r,i)merge(x(id(i)),y(id(i)));
rep(1,m1,i)
{
if(!vis[b[i]])
{
//cout<<b[i]<<endl;
if(merge(x(b[i]),y(b[i])))
{
b[i]=-b[i];
}
}
}
rep(1,n,i)f[a[i]]=F[d-1][a[i]],sz[a[i]]=SZ[d-1][a[i]];
ll ww=0;int m2=0;
rep(1,m1,i)
{
if(b[i]<0)
{
b[i]=-b[i];
merge(x(b[i]),y(b[i]));
ww+=z(b[i]);
//cout<<b[i]<<' '<<z(b[i])<<endl;
}
else b[++m2]=b[i];
}
int n1=0;
rep(1,n,i)if(getfather(a[i])==a[i])A[d][++n1]=a[i];
rep(1,m2,i)
{
W1[d][b[i]]=x(b[i]);
W2[d][b[i]]=y(b[i]);
x(b[i])=getfather(x(b[i]));
y(b[i])=getfather(y(b[i]));
}
if(l==r)
{
z(id(l))=w(r);int cc=w(r);
rep(1,m2,i)cc=min(cc,z(b[i]));
putl(ans+ww+cc);
rep(l,r,i)vis[id(i)]=0;
b=B[d];
rep(1,m2,i)
{
x(b[i])=W1[d][b[i]];
y(b[i])=W2[d][b[i]];
}
return;
}
int mid=(l+r)>>1;
rep(l,r,i)vis[id(i)]=0;
CDQ(d+1,l,mid,n1,m2,ans+ww);
CDQ(d+1,mid+1,r,n1,m2,ans+ww);
b=B[d];
rep(1,m2,i)
{
x(b[i])=W1[d][b[i]];
y(b[i])=W2[d][b[i]];
}
}
int main()
{
freopen("1.in","r",stdin);
sc(n);sc(m);sc(q);b=B[0];
rep(1,m,i){sc(x(i));sc(y(i));sc(z(i));b[i]=i;}
rep(1,q,i){sc(id(i));sc(w(i));}
f=F[0];sz=SZ[0];a=A[0];
rep(1,n,i)f[i]=i,sz[i]=1,a[i]=i;
//sort(a+1,a+1+n,cmp);
CDQ(1,1,q,n,m,0);
return 0;
}