• BZOJ3648 : 寝室管理


    求环套外向树上节点数不小于K的路径数。

    首先树的话直接点分治+树状数组$O(nlog^2n)$搞定

    环套树的话,先删掉多余的边(a,b)

    然后变成了一棵树,直接点分治

    然后在树上找到a到b的路径,将每一棵子树中的点的“所有权”(要么从a出发到达,要么从b出发到达)改变一下,然后计算贡献即可。

    总时间复杂度$O(nlog^2n+nlog n)$

    把BZOJ第700题献给了这道题…

    #include<cstdio>
    #define N 100010
    typedef long long ll;
    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 n,m,K,i,x,y,g[N],nxt[N<<1],v[N<<1],ok[N<<1],ed=1,son[N],f[N],size,now,fa[N],exa,exb,to[N],way[N],cnt,in[N],dis[N],t[N],pos;
    ll ans,bit[N];
    inline void addedge(int x,int y){v[++ed]=y,nxt[ed]=g[x],ok[ed]=1,g[x]=ed;}
    int F(int x){return fa[x]==x?x:fa[x]=F(fa[x]);}
    inline void add(int x){for(;x<=n;x+=x&-x)if(t[x]!=pos)t[x]=pos,bit[x]=1;else bit[x]++;}
    inline void del(int x){for(;x<=n;x+=x&-x)bit[x]--;}
    inline ll sum(int x){if(x<0)return 0;ll y=0;for(;x;x-=x&-x)if(t[x]==pos)y+=bit[x];return y;}
    void findroot(int x,int pre){
      son[x]=1;f[x]=0;
      for(int i=g[x];i;i=nxt[i])if(ok[i]&&v[i]!=pre){
        findroot(v[i],x);
        son[x]+=son[v[i]];
        if(son[v[i]]>f[x])f[x]=son[v[i]];
      }
      if(size-son[x]>f[x])f[x]=size-son[x];
      if(f[x]<f[now])now=x;
    }
    void dfscal(int x,int pre,int dep){
      ans+=sum(n)-sum(K-dep-1);
      for(int i=g[x];i;i=nxt[i])if(ok[i]&&v[i]!=pre)dfscal(v[i],x,dep+1);
    }
    void dfsadd(int x,int pre,int dep){
      add(dep+1);
      for(int i=g[x];i;i=nxt[i])if(ok[i]&&v[i]!=pre)dfsadd(v[i],x,dep+1);
    }
    void solve(int x){
      int i;
      pos++,add(1);
      for(i=g[x];i;i=nxt[i])if(ok[i])dfscal(v[i],x,1),dfsadd(v[i],x,1);
      for(i=g[x];i;i=nxt[i])if(ok[i])ok[i^1]=0,f[0]=size=son[v[i]],findroot(v[i],now=0),solve(now);
    }
    void path(int x,int pre,int d){
      to[x]=pre,add(dis[x]=d);
      for(int i=g[x];i;i=nxt[i])if(v[i]!=pre)path(v[i],x,d+1);
    }
    void treecal(int x,int pre,int d){
      ans+=sum(n)-sum(K-d-1);
      for(int i=g[x];i;i=nxt[i])if(v[i]!=pre&&!in[v[i]])treecal(v[i],x,d+1);
    }
    void treedel(int x,int pre){
      del(dis[x]);
      for(int i=g[x];i;i=nxt[i])if(v[i]!=pre&&!in[v[i]])treedel(v[i],x);
    }
    int main(){
      read(n),read(m),read(K);
      for(i=1;i<=n;i++)fa[i]=i;
      while(m--){
        read(x),read(y);
        if(F(x)==F(y))exa=x,exb=y;else fa[fa[x]]=fa[y],addedge(x,y),addedge(y,x);
      }
      f[0]=size=n,findroot(1,now=0),solve(now);
      if(exa){
        pos++,path(exa,0,1);
        for(i=exb;i;i=to[i])in[way[++cnt]=i]=1;
        for(i=1;i<cnt;i++)treedel(way[i],0),treecal(way[i],0,i);
      }
      return printf("%lld",ans),0;
    }
    

      

  • 相关阅读:
    2020/3/12
    练习题1
    2020/3/26
    2020/3/25
    2020/3/24
    2020/3/23
    应用层
    bzoj3326[SCOI2013]数数
    HEOI2017游记
    bzoj4417[SHOI2013]超级跳马
  • 原文地址:https://www.cnblogs.com/clrs97/p/4403228.html
Copyright © 2020-2023  润新知