BZOJ 2001: [Hnoi2010]City 城市建设

好鬼的CDQ分治,感觉复杂度好迷的说感觉就是个剪枝的暴力

首先看到题目,动态MST,妈妈我会线段树分治+LCT然后这题就做完了

大体上很套路,我们把修改看作一条边的删除以及一条新边的加入,就可以求出每条边出现的时间区间

然后按时间为下标建线段树,我们只要能实现插入一条边/撤销即可,然后我们发现这个东西可以很容易LCT维护,每次找出成环的路径上的最大值然后替换掉即可

总体复杂度\(O(n\log^2 n)\),据说常数巨大(我不想写),因此这里讲一种抄来的玄学CDQ分治的做法

首先我们要明确CDQ的本质:讲可以一些操作间重复的部分最大化,然后解决这个重复部分

考虑我们现在处理的区间是\([l,r]\),那么对于不被这个区间操作影响的边(姑且称为静态边),显然我们可以找出其中一些必须选的边和一些肯定不选的边,然后再往下递归的时候就不用考虑它们

那具体怎么处理呢,我们考虑进行以下操作:

  1. 将现在分治的区间内的所有边(姑且称为动态边),边权设为\(-\infty\),然后跑一遍MST,然后我们找出MST上的所有静态边(边权不为\(-\infty\)),它们显然是必选的
  2. 将现在分治的区间内的所有边,边权设为\(\infty\),然后跑一遍MST,然后我们找出没有出现在MST上的所有静态边(边权不为\(\infty\)),它们显然是无用的

然后我们把所有的必选边先连起来,然后往下处理的时候忽略掉无用边

根据某种神奇的力量(或者感性理解),这样每次处理的边数会减半(会证明的老哥麻烦告诉我下证明),因此总复杂度就是\(O(n\log^2 n)\)的,但是常数极小(分治+排序),不像某两大常数算法的结合

#include<cstdio>
#include<cctype>
#include<vector>
#include<algorithm>
#define RI register int
#define CI const int&
#define Tp template <typename T>
#define pb push_back
using namespace std;
typedef long long LL;
const int N=50005,INF=1e9;
struct edge
{
	int x,y,w,id;
	friend inline bool operator < (const edge& A,const edge& B)
	{
		return A.w<B.w;
	}
}ne[N],tp[N],nw; vector <edge> E[20]; int n,m,q,val[N],px[N],py[N],pos[N]; LL ans[N];
class FileInputOutput
{
	private:
		static const int S=1<<21;
		#define tc() (A==B&&(B=(A=Fin)+fread(Fin,1,S,stdin),A==B)?EOF:*A++)
		#define pc(ch) (Ftop!=Fend?*Ftop++=ch:(fwrite(Fout,1,S,stdout),*(Ftop=Fout)++=ch))
		char Fin[S],Fout[S],*A,*B,*Ftop,*Fend; int pt[25];
	public:
		inline FileInputOutput(void) { Ftop=Fout; Fend=Fout+S; }
		Tp inline void read(T& x)
		{
			x=0; char ch; while (!isdigit(ch=tc()));
			while (x=(x<<3)+(x<<1)+(ch&15),isdigit(ch=tc()));
		}
		Tp inline void write(T x)
		{
			RI ptop=0; while (pt[++ptop]=x%10,x/=10);
			while (ptop) pc(pt[ptop--]+48); pc('\n');
		}
		inline void flush(void)
		{
			fwrite(Fout,1,Ftop-Fout,stdout);
		}
		#undef tc
		#undef pc
}F;
class UnionFindSet
{
	private:
		int fa[N];
	public:
		inline void init(CI n,edge *e)
		{
			for (RI i=0;i<n;++i) fa[e[i].x]=e[i].x,fa[e[i].y]=e[i].y;
		}
		inline int getfa(CI x)
		{
			return fa[x]!=x?fa[x]=getfa(fa[x]):x;
		}
		inline bool identify(CI x,CI y)
		{
			return getfa(x)==getfa(y);
		}
		inline void Union(CI x,CI y)
		{
			fa[getfa(x)]=getfa(y);
		}
}UFS;
inline void retain(int& n,LL& sum)
{
	RI i,cnt=0; for (UFS.init(n,ne),sort(ne,ne+n),i=0;i<n;++i)
	if (!UFS.identify(ne[i].x,ne[i].y)) UFS.Union(ne[i].x,ne[i].y),tp[cnt++]=ne[i];
	for (UFS.init(cnt,tp),i=0;i<cnt;++i) if (tp[i].w!=-INF)
	if (!UFS.identify(tp[i].x,tp[i].y)) UFS.Union(tp[i].x,tp[i].y),sum+=tp[i].w;
	for (cnt=i=0;i<n;++i) if (!UFS.identify(ne[i].x,ne[i].y))
	tp[cnt++]=(edge){UFS.getfa(ne[i].x),UFS.getfa(ne[i].y),ne[i].w,ne[i].id};
	for (i=0;i<cnt;++i) pos[ne[i].id]=i,ne[i]=tp[i]; n=cnt;
}
inline void remove(int& n)
{
	RI i,cnt=0; for (UFS.init(n,ne),sort(ne,ne+n),i=0;i<n;++i)
	if (!UFS.identify(ne[i].x,ne[i].y)) UFS.Union(ne[i].x,ne[i].y),tp[cnt++]=ne[i];
	else if (ne[i].w==INF) tp[cnt++]=ne[i];
	for (i=0;i<cnt;++i) pos[ne[i].id]=i,ne[i]=tp[i]; n=cnt;
}
inline void solve(CI l=1,CI r=q,CI dep=0,LL sum=0)
{
	int n=E[dep].size(); RI i; if (l==r) val[px[l]]=py[l];
	for (i=0;i<n;++i) E[dep][i].w=val[E[dep][i].id],ne[i]=E[dep][i],pos[ne[i].id]=i;
	if (l==r)
	{
		for (ans[l]=sum,UFS.init(n,ne),sort(ne,ne+n),i=0;i<n;++i)
		if (!UFS.identify(ne[i].x,ne[i].y)) UFS.Union(ne[i].x,ne[i].y),ans[l]+=ne[i].w; return;
	}
	for (i=l;i<=r;++i) ne[pos[px[i]]].w=-INF; retain(n,sum);
	for (i=l;i<=r;++i) ne[pos[px[i]]].w=INF; remove(n);
	for (E[dep+1].clear(),i=0;i<n;++i) E[dep+1].pb(ne[i]);
	int mid=l+r>>1; solve(l,mid,dep+1,sum); solve(mid+1,r,dep+1,sum);
}
int main()
{
	//freopen("CODE.in","r",stdin); freopen("CODE.out","w",stdout);
	RI i; for (F.read(n),F.read(m),F.read(q),i=1;i<=m;++i)
	F.read(nw.x),F.read(nw.y),F.read(nw.w),val[i]=nw.w,nw.id=i,E[0].pb(nw);
	for (i=1;i<=q;++i) F.read(px[i]),F.read(py[i]);
	for (solve(),i=1;i<=q;++i) F.write(ans[i]); return F.flush(),0;
}
posted @ 2020-02-04 18:59  空気力学の詩  阅读(298)  评论(0编辑  收藏  举报