【GDOI 2016】机密网络

Description

【GDOI 2016】机密网络_第1张图片

Solution

环套外向树分治!!!

很有趣,比赛时打了个暴搜就过了。
以后还有仙人掌分治(瞎猜)怎么办……

幸好只有一个环

很容易想到,在环上拆掉一条边,然后求点对,在计算只通过这条边的点对,就好了!!!

问题来了,只通过这条边的点对要怎么统计

正难则反。
只通过这条边的点对=删边前可以的点对-删边后和删边前都可以的点对。
所以,删边前可以的点对=ans=只通过这条边的点对+删边后和删边前都可以的点对

只通过这条边的点对

设删掉的边是(tou,wei),有数组aa[i]表示每个点到tou的距离,bb[i]表示每个点到wei的距离。
把aa[i]

删边后和删边前都可以的点对

可以用正常的点分治计算删掉一条边后,然后再顺便用上面的方法统计符合当前树分治条件,又符合通过删去的那条边的条件,当然也是用树状数组来统计。

这道题要注意常数,能省的尽量省

我就被一个无关紧要的常数优化卡掉了60分TAT。。。

Code

#include
#include
#include
#include
#include
#define fo(i,a,b) for(i=a;i<=b;i++)
#define rep(i,a) for(i=first[a];i;i=next[i])
using namespace std;
typedef long long ll;
const int maxn=100007;
int i,j,k,l,n,m;
ll a[maxn],ans,ans1,t[maxn][2],yi,er,san;
int b[maxn],tou,wei;
int first[maxn*2],next[maxn*2],last[maxn*2],num,size[maxn];
int aa[maxn],bb[maxn],c[maxn],z,deep[maxn],f[maxn],g[maxn];
bool bz[maxn],az[maxn];
void add(int x,int y){
    last[++num]=y;next[num]=first[x];first[x]=num;
}
void huan(int x,int y){
    int i;
    if(bz[x])return;
    bz[x]=1;
    rep(i,x){
        if(last[i]!=y){
            if(bz[last[i]]){
                tou=x,wei=last[i];
                return;
            }
            huan(last[i],x);
        }
    }
}
void dfs(int x,int y){
    int i;
    rep(i,x){
        if(last[i]!=y){
            aa[last[i]]=aa[x]+1;
            dfs(last[i],x);
        }
    }
}
void zhao(int x,int y){
    int i;size[x]=1;
    rep(i,x){
        if(last[i]!=y&&!az[last[i]]){
            zhao(last[i],x);
            size[x]+=size[last[i]];
        }
    }
}
void zhaozhong(int x,int y){
   int i;bool dz=1;
   rep(i,x){
       if(last[i]!=y&&!az[last[i]]){
           zhaozhong(last[i],x);
           if(size[last[i]]>yi/2)dz=0;
       }
   }
   if(yi-size[x]<=yi/2&&dz)z=x;
}
void zhaoshen(int x,int y){
    f[++f[0]]=x,g[++g[0]]=x;
    deep[x]=deep[y]+1;
    int i;
    rep(i,x){
        if(last[i]!=y&&!az[last[i]]){
            zhaoshen(last[i],x);
        }
    }
}
bool cmp(int x,int y){
    return deep[x]y];
}
int lowbit(int x){
    return (-x)&x;
}
void tadd(int x,int y,ll z){
    int i;
    for(i=x+1;i<=n;i+=lowbit(i)){
        t[i][0]+=y;
        t[i][1]+=z;
    }
}
ll find(int x,int y){
    int i;ll j=0;
    for(i=x+1;i>0;i-=lowbit(i)){
        j+=t[i][y];
    }
    return j;
}
void doing(int *x,int y){
    ll i;ll k=0;
    sort(x+1,x+1+x[0],cmp);
    fo(i,1,x[0]){
        if(aa[x[i]]x[i]]){
            tadd(aa[x[i]],1,a[x[i]]);
        }
        k+=a[x[i]];
    }
    int o=x[0];
    fo(i,1,x[0]){
        while(o&&deep[x[i]]+deep[x[o]]>m){
            if(aa[x[o]]x[o]]){
                tadd(aa[x[o]],-1,-a[x[o]]);
            }
            k-=a[x[o--]];
        }
        if(!o)break;
        er+=o*y;
        san+=a[x[i]]*y*k;
        if(o&&aa[x[i]]>bb[x[i]]){
            ans-=find(m-bb[x[i]]-1,0)*y;
            ans1-=a[x[i]]*find(m-bb[x[i]]-1,1)*y;
        }
    }
    fo(i,1,o){
        if(aa[x[i]]x[i]]){
            tadd(aa[x[i]],-1,-a[x[i]]);
        }
    }
}
int get() {
    char ch;
    while (!isdigit(ch=getchar()));
    int o=ch-48;
    while (isdigit(ch=getchar())) o=o*10+ch-48;
    return o;
}
void fenzhi(int x){
    int i;
    zhao(x,0);
    yi=size[x],er=san=0;
    zhaozhong(x,0);
    az[z]=1;f[f[0]=1]=z;
    deep[z]=0;
    rep(i,z){
        if(!az[last[i]]){
            deep[last[i]];g[0]=0;
            zhaoshen(last[i],z);
            doing(g,-1);
        }
    }
    doing(f,1);
    ans+=(er-1)/2;
    ans1+=(san-a[z]*a[z])/2;
    rep(i,z){
        if(!az[last[i]]){
            fenzhi(last[i]);
        }
    }
}
int main(){
   // freopen("pronet.in","r",stdin);
   // freopen("pronet.out","w",stdout);
   // freopen("fan.in","r",stdin);
    n=get();m=get();
    fo(i,1,n){
        b[i]=get();
        add(i,b[i]),add(b[i],i);
    }   
    fo(i,1,n)a[i]=get();
    huan(1,0);num=0;memset(first,0,sizeof(first));
    fo(i,1,n){
        if(i==tou&&b[i]==wei||i==wei&&b[i]==tou)continue;
        add(i,b[i]);add(b[i],i);
    }
    dfs(wei,0);
    memcpy(bb,aa,sizeof(bb));aa[tou]=0;
    dfs(tou,0);
    fenzhi(1); 
    fo(i,1,n){
        if(aa[i]1,a[i]);
        }
    }
    fo(i,1,n){
        if(aa[i]>bb[i]){
            ans+=find(m-bb[i]-1,0);
            ans1+=a[i]*find(m-bb[i]-1,1);    
        }
    }
    printf("%lld %lld\n",ans,ans1);
}

你可能感兴趣的:(树,GDOI,树状数组,树分治)