BZOJ4046 : [Cerc2014] Pork barre

按边权从大到小加边,用Link-Cut Tree维护最小生成树。

对于当前要加的边i,最小生成树上边权在[1,R]范围内的和就是询问[e[i].w,R]的答案。

因为强制在线,所以用主席树存下所有历史版本即可。时间复杂度$O(n\log n)$。

 

#include<cstdio>
#include<algorithm>
using namespace std;
const int N=101010,M=3600010;
struct edge{int x,y,w;}e[N];
inline bool cmp(edge a,edge b){return a.w>b.w;}
int T,n,m,q,i,j,x,y,ans,root[N],l[M],r[M],v[M],tot;
int f[N],son[N][2],val[N],sum[N],from[N],tmp[N],fa[N];bool rev[N];
int ins(int x,int a,int b,int c,int d){
  int y=++tot;
  v[y]=v[x]+d,l[y]=l[x],r[y]=r[x];
  if(a==b)return y;
  int mid=(a+b)>>1;
  if(c<=mid)l[y]=ins(l[x],a,mid,c,d);else r[y]=ins(r[x],mid+1,b,c,d);
  return y;
}
int ask(int x,int a,int b,int c,int d){
  if(!x||c>d)return 0;
  if(c<=a&&b<=d)return v[x];
  int mid=(a+b)>>1,t=0;
  if(c<=mid)t=ask(l[x],a,mid,c,d);
  if(d>mid)t+=ask(r[x],mid+1,b,c,d);
  return t;
}
inline bool isroot(int x){return !f[x]||(son[f[x]][0]!=x&&son[f[x]][1]!=x);}
inline void rev1(int x){if(!x)return;swap(son[x][0],son[x][1]);rev[x]^=1;}
inline void pb(int x){if(rev[x])rev1(son[x][0]),rev1(son[x][1]),rev[x]=0;}
inline void up(int x){
  sum[x]=val[x],from[x]=x;
  if(son[x][0])if(sum[son[x][0]]>sum[x])sum[x]=sum[son[x][0]],from[x]=from[son[x][0]];
  if(son[x][1])if(sum[son[x][1]]>sum[x])sum[x]=sum[son[x][1]],from[x]=from[son[x][1]];
}
inline void rotate(int x){
  int y=f[x],w=son[y][1]==x;
  son[y][w]=son[x][w^1];
  if(son[x][w^1])f[son[x][w^1]]=y;
  if(f[y]){
    int z=f[y];
    if(son[z][0]==y)son[z][0]=x;else if(son[z][1]==y)son[z][1]=x;
  }
  f[x]=f[y];f[y]=x;son[x][w^1]=y;up(y);
}
inline void splay(int x){
  int s=1,i=x,y;tmp[1]=i;
  while(!isroot(i))tmp[++s]=i=f[i];
  while(s)pb(tmp[s--]);
  while(!isroot(x)){
    y=f[x];
    if(!isroot(y)){if((son[f[y]][0]==y)^(son[y][0]==x))rotate(x);else rotate(y);}
    rotate(x);
  }
  up(x);
}
inline void access(int x){for(int y=0;x;y=x,x=f[x])splay(x),son[x][1]=y,up(x);}
inline void makeroot(int x){access(x);splay(x);rev1(x);}
inline void link(int x,int y){makeroot(x);f[x]=y;access(x);}
inline void cutf(int x){access(x);splay(x);f[son[x][0]]=0;son[x][0]=0;up(x);}
inline void cut(int x,int y){makeroot(x);cutf(y);}
inline int askfrom(int x,int y){makeroot(x);access(y);splay(y);return from[y];}
int F(int x){return fa[x]==x?x:fa[x]=F(fa[x]);}
inline int lower(int x){
  int l=1,r=m,mid,t=0;
  while(l<=r)if(e[mid=(l+r)>>1].w<=x)r=(t=mid)-1;else l=mid+1;
  return t;
}
inline int getid(int x){
  int l=1,r=m,mid,t=0;
  while(l<=r)if(e[mid=(l+r)>>1].w>=x)l=(t=mid)+1;else r=mid-1;
  return t;
}
inline void read(int&a){char c;while(!(((c=getchar())>='0')&&(c<='9')));a=c-'0';while(((c=getchar())>='0')&&(c<='9'))(a*=10)+=c-'0';}
int main(){
  read(T);
  while(T--){
    read(n),read(m);
    for(i=1;i<=n;i++)fa[i]=i;
    for(i=1;i<=m;i++)read(e[i].x),read(e[i].y),read(e[i].w);
    sort(e+1,e+m+1,cmp);
    for(i=1;i<=m;i++)val[n+i]=sum[n+i]=e[i].w,from[n+i]=n+i;
    for(i=1;i<=m;i++){
      root[i]=root[i-1];
      if(F(e[i].x)==F(e[i].y)){
        j=askfrom(e[i].x,e[i].y);
        cut(j,e[j-n].x),cut(j,e[j-n].y);
        root[i]=ins(root[i],1,m,j-n,-e[j-n].w);
      }else fa[fa[e[i].x]]=fa[e[i].y];
      link(e[i].x,n+i),link(e[i].y,n+i);
      root[i]=ins(root[i],1,m,i,e[i].w);
    }
    read(q);
    while(q--){
      read(x),read(y),x-=ans,y-=ans;
      printf("%d\n",ans=ask(root[getid(x)],1,m,lower(y),m));
    }
    for(i=tot=ans=0;i<=n+m;i++)f[i]=son[i][0]=son[i][1]=val[i]=sum[i]=from[i]=rev[i]=0;
    for(i=1;i<=n;i++)fa[i]=0;
    for(i=1;i<=m;i++)root[i]=0;
  }
  return 0;
}

  

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值