BZOJ 2001: [Hnoi2010]City 城市建设
作者:互联网
好鬼的CDQ分治,感觉复杂度好迷的说感觉就是个剪枝的暴力
首先看到题目,动态MST,妈妈我会线段树分治+LCT,然后这题就做完了
大体上很套路,我们把修改看作一条边的删除以及一条新边的加入,就可以求出每条边出现的时间区间
然后按时间为下标建线段树,我们只要能实现插入一条边/撤销即可,然后我们发现这个东西可以很容易LCT维护,每次找出成环的路径上的最大值然后替换掉即可
总体复杂度\(O(n\log^2 n)\),据说常数巨大(我不想写),因此这里讲一种抄来的玄学CDQ分治的做法
首先我们要明确CDQ的本质:讲可以一些操作间重复的部分最大化,然后解决这个重复部分
考虑我们现在处理的区间是\([l,r]\),那么对于不被这个区间操作影响的边(姑且称为静态边),显然我们可以找出其中一些必须选的边和一些肯定不选的边,然后再往下递归的时候就不用考虑它们
那具体怎么处理呢,我们考虑进行以下操作:
- 将现在分治的区间内的所有边(姑且称为动态边),边权设为\(-\infty\),然后跑一遍MST,然后我们找出MST上的所有静态边(边权不为\(-\infty\)),它们显然是必选的
- 将现在分治的区间内的所有边,边权设为\(\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;
}
标签:City,分治,const,int,MST,然后,Hnoi2010,include,2001 来源: https://www.cnblogs.com/cjjsb/p/12260586.html