题目概述
给出
n
个点
- 加入
a
到
b 长度为 i (i 是该操作的标号)的边。 - 删除边权前 k 大的边。
- 返回到
i−2 状态。
求每次操作后最小生成树的权值。
解题报告
刚开始看成第k大的边,然后……
回退?可持久化啊!其实这道题用离线会非常简单……我们先考虑没有返回操作的情况,由于边权从小到大,所以每次删除就是删除最近的
k
条边,又因为每条边只会被删除一次,所以用按秩合并的并查集就可以在
但是有返回操作,我们离线,直接检查 i 操作后面是不是返回操作,如果是的话(不是就直接处理):
i 操作是加入:先加入,输出答案,然后回退。-
i
操作是删除:预处理出用前
k 小边的最小生成树的权值 ans[k] ,输出答案,不执行删除操作。 示例程序
#include<cstdio> using namespace std; typedef long long LL; const int maxn=300000,maxm=500000; int n,m,a[maxm+5],b[maxm+5];char td[maxm+5]; int E,fa[maxn+5],rk[maxn+5]; struct data {int a,b,fa,ra,fb,rb;}; data lst[maxm+5];LL ans[maxm+5];int num[maxm+5]; #define Eoln(x) ((x)==10||(x)==13||(x)==EOF) inline char readc() { static char buf[100000],*l=buf,*r=buf; if (l==r) r=(l=buf)+fread(buf,1,100000,stdin); if (l==r) return EOF;return *l++; } inline int readi(int &x) { int tot=0,f=1;char ch=readc(),lst='+'; while ('9'<ch||ch<'0') {if (ch==EOF) return EOF;lst=ch;ch=readc();} if (lst=='-') f=-f; while ('0'<=ch&&ch<='9') tot=(tot<<3)+(tot<<1)+ch-48,ch=readc(); return x=tot*f,Eoln(ch); } inline void writel(LL x) { static int buf[20],len;len=0;do buf[len++]=x%10,x/=10; while (x); while (len) putchar(buf[--len]+48);putchar('\n'); } inline char getrch() {char ch=readc();while ('Z'<ch||ch<'A') ch=readc();return ch;} int getfa(int x) {if (fa[x]==x) return x;return getfa(fa[x]);} inline void Back(int E) { fa[lst[E].a]=lst[E].fa;rk[lst[E].a]=lst[E].ra; fa[lst[E].b]=lst[E].fb;rk[lst[E].b]=lst[E].rb; } inline void Add(int i) { int fx=getfa(a[i]),fy=getfa(b[i]); ans[E+1]=ans[E];num[E+1]=num[E]; lst[++E]=(data){fx,fy,fa[fx],rk[fx],fa[fy],rk[fy]}; if (fx==fy) writel(num[E]<n-1?0:ans[E]); else { ans[E]+=i;num[E]++; if (rk[fx]<rk[fy]) fa[fx]=fy; else if (rk[fx]>rk[fy]) fa[fy]=fx; else rk[fy]++,fa[fx]=fy;writel(num[E]<n-1?0:ans[E]); } if (i+1<=m&&td[i+1]=='R') Back(E--),writel(num[E]<n-1?0:ans[E]); } inline void Delete(int i) { int pos=E-a[i];writel(num[pos]<n-1?0:ans[pos]); if (i+1<=m&&td[i+1]=='R') return writel(num[E]<n-1?0:ans[E]); while (E>pos) Back(E--); } int main() { freopen("program.in","r",stdin); freopen("program.out","w",stdout); readi(n);readi(m); for (int i=1;i<=m;i++) switch(td[i]=getrch()) { case 'A':readi(a[i]),readi(b[i]);break; case 'D':readi(a[i]);break; } for (int i=1;i<=n;i++) fa[i]=i; for (int i=1;i<=m;i++) if (td[i]=='A') Add(i); else if (td[i]=='D') Delete(i); return 0; }
-
i
操作是删除:预处理出用前