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;
}
posted @ 2022-11-22 18:06  chdy  阅读(58)  评论(0编辑  收藏  举报