完全动态最小生成树(sgu529,HNOI2010city)

对于加边和删边改变成无穷大就好了。

话说HN好喜欢考裸的比较偏的模板题。会就会,不会考场很难想出来吧。

思想很好理解,就是对询问分治,然后缩点递归。和AHOI2013有道题比较类似。

具体实现可以看英文论文和GYZ在WC上讲课的PPT。

网上的代码都是nlog^2 n的,于是我就去写了nlogn的,结果编程能力太弱。

导致代码太丑,常数巨大.实际运行效果不明显。

而且说是完全动态,但是对于动态维护MST,以及维护路径路径问题

分治貌似莫法搞(WC2006),好像只能写动态树

#include<cstdio>
#include<cstring>
#include<algorithm>
#define N 200010
using namespace std;
struct Que
{int k,d;}h[N];
struct Num
{int a,b;}num[N];
struct Ege
{int a,b,c,w;}Eg[N],eg[20][N],neg[20][N],nEg[N];
int falg,n,m,q,use[N],have[N],tot[20],t1,t2[20],f[N];
bool cmp(Ege a,Ege b)
{
 return a.c<b.c;
}
int find(int v)
{
 if (f[v]==v) return f[v];
 f[v]=find(f[v]);
 return f[v];
}
void YES(int t)
{
 for (int i=1;i<=m;i++)
  {
    f[eg[t][i].a]=eg[t][i].a;
    f[eg[t][i].b]=eg[t][i].b;
  }
 for (int i=1;i<=m;i++)
  if (use[eg[t][i].w]==1)
   f[find(eg[t][i].a)]=find(eg[t][i].b);
 for (int i=1;i<=m;i++)
  if (use[eg[t][i].w]!=1&&find(eg[t][i].a)!=find(eg[t][i].b))
    {
     use[eg[t][i].w]=2;
     f[find(eg[t][i].a)]=find(eg[t][i].b);
    }
}
void No(int t)
{
 for (int i=1;i<=m;i++)
  {
    have[eg[t][i].a]=have[eg[t][i].b]=0;
    f[eg[t][i].a]=eg[t][i].a;
    f[eg[t][i].b]=eg[t][i].b;
  }
 int n=0;
 for (int i=1;i<=m;i++)
  {
   if (have[eg[t][i].a]==0) {n++;have[eg[t][i].a]=1;}
   if (have[eg[t][i].b]==0) {n++;have[eg[t][i].b]=1;}
  }
 int tot=0,k=m;
 for (int i=1;i<=m;i++)
  if (use[eg[t][i].w]!=1)
  {
   if (f[find(eg[t][i].a)]!=find(eg[t][i].b))
   {
    f[find(eg[t][i].a)]=find(eg[t][i].b);
    tot++;
    if (n-1==tot) {k=i;break;}
   } else use[eg[t][i].w]=3;
  }
 for (int i=k+1;i<=m;i++)
  if (!use[eg[t][i].w]) 
   use[eg[t][i].w]=3;
   
}
int kru(int k,int d,int t)
{
 if (tot[t]==1) return d;
 if (eg[t][1].w==k) eg[t][1].c=d;
  else eg[t][2].c=d;
 if (eg[t][1].c<eg[t][2].c) return eg[t][1].c;
 return eg[t][2].c;
}
void hebin(int t,int m1)
{
 int l1=1,l2=1,m2=t1;
 for (int i=1;i<=m1;i++)
  {
   use[eg[t][i].w]=0;
   num[eg[t][i].w].a=eg[t][i].a;
   num[eg[t][i].w].b=eg[t][i].b;
  }
 for (int i=1;i<=m2;i++)
  use[Eg[i].w]=1;
 for (int i=1;i<=m1;i++)
  {
   while (l1<=m1&&use[eg[t][l1].w])l1++;
   if (l1>m1||(l2<=m2&&Eg[l2].c<eg[t][l1].c)) nEg[i]=Eg[l2++];
     else nEg[i]=eg[t][l1++];
  }       
 for (int i=1;i<=m1;i++)
  {
    eg[t][i]=nEg[i];
    eg[t][i].a=num[eg[t][i].w].a;
    eg[t][i].b=num[eg[t][i].w].b;
  }
}
void deal(int l,int r,int t,long long sum)
{
 m=tot[t];
 for (int i=1;i<=m;i++)
  use[eg[t][i].w]=0;
 for (int i=l;i<=r;i++)
   use[h[i].k]=1;
 YES(t);No(t);
 falg=0;
 for (int i=1;i<=m;i++)
 {
  f[eg[t][i].a]=eg[t][i].a;
  f[eg[t][i].b]=eg[t][i].b;
 }
 int &nm=tot[t+1];
 nm=0;
 for (int i=1;i<=m;i++)
  if (use[eg[t][i].w]==2)
   {
     f[find(eg[t][i].a)]=find(eg[t][i].b);
     sum+=eg[t][i].c;
   }
 for (int i=1;i<=m;i++)
  if (use[eg[t][i].w]==0||use[eg[t][i].w]==1&&find(eg[t][i].a)!=find(eg[t][i].b))
   {
    ++nm;
    eg[t+1][nm].a=find(eg[t][i].a);
    eg[t+1][nm].b=find(eg[t][i].b);
    eg[t+1][nm].c=eg[t][i].c; 
    eg[t+1][nm].w=eg[t][i].w;
   }
 
 if (l==r)
  {
   printf("%I64d\n",sum+kru(h[l].k,h[l].d,t+1));
   t1=1;
   Eg[t1].c=h[l].d;Eg[t1].w=h[l].k;
   return ;
  }
 int mid=(l+r)/2;
 deal(l,mid,t+1,sum);
 t2[t]=t1;
 for (int i=1;i<=t1;i++)
  neg[t][i]=Eg[i];
 hebin(t+1,nm);
 t1=0;
 deal(mid+1,r,t+1,sum);
 int l1=1,l2=1,t3=0;
 for (int i=1;i<=t2[t];i++)
  use[neg[t][i].w]=0;
 for (int i=1;i<=t1;i++)
  use[Eg[i].w]=1;
 while (l1<=t1||l2<=t2[t])
 {
  while (l2<=t2[t]&&use[neg[t][l2].w])l2++;
  if (l1>t1&&l2>t2[t]) break;
  if (l2>t2[t]||(l1<=t1&&Eg[l1].c<neg[t][l2].c)) nEg[++t3]=Eg[l1++];
   else nEg[++t3]=neg[t][l2++];
 }
 t1=t3;
 for (int i=1;i<=t1;i++)
  Eg[i]=nEg[i]; 
}
int main()
{
 scanf("%d%d%d",&n,&m,&q);
 for (int i=1;i<=m;i++)
  {
   scanf("%d%d%d",&Eg[i].a,&Eg[i].b,&Eg[i].c);
   Eg[i].w=i;
  }
 for (int i=1;i<=q;i++)
  scanf("%d%d",&h[i].k,&h[i].d);
 sort(Eg+1,Eg+1+m,cmp);
 tot[0]=m;
 for(int i=1;i<=m;i++)
  eg[0][i]=Eg[i];
 deal(1,q,0,0);
 return 0;
}


评论 9
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值