Tree(树分治 :点分治)

原题:POJ-1741

题意:

有一棵n个节点的树,每条边都有一个权值,问有多少个节点之间的距离小于等于k,

解析:


典型的树分治,对于每一棵树,我们首先找到它的重心
重心:一棵树中以这个点为root时的最大子树的节点数最小

int siz[N],maxn[N];//这棵子树大小,最大子树大小
void getG(int p,int fa,int sum){
    siz[p]=1;
    maxn[p]=0;
    for(int i=head[p];~i;i=nex[i]){
        if(!vis[to[i]]&&to[i]!=fa){
            getG(to[i],p,sum);//这个时候如果重心在下面可以得出G,也可以得出下面的siz

            siz[p]+=siz[to[i]];
            maxn[p]=max(maxn[p],siz[to[i]]);//找出最大子树
        }
    }
    maxn[p]=max(maxn[p],sum-siz[p]);//当然还要这棵树的上半部分比较

    if(maxn[G]>maxn[p])G=p;
}

然后计算和这个节点有关的题目中要求的东西,然后就可以删除这个点,变成很多棵树,在对每棵子树进行同样的操作

其他的就看题目写了

代码:

const int N =10009;
int n,k;
int head[N],nex[2*N],to[2*N],from[2*N],v[2*N],now;
void add(int a,int b,int vv){
    to[++now]=b;v[now]=vv;from[now]=a;nex[now]=head[a];head[a]=now;
    to[++now]=a;v[now]=vv;from[now]=b;nex[now]=head[b];head[b]=now;
}

int ans;

int G;
int vis[N];//是否删除
int siz[N],maxn[N];//这棵子树大小,最大子树大小
void getG(int p,int fa,int sum){
    siz[p]=1;
    maxn[p]=0;
    for(int i=head[p];~i;i=nex[i]){
        if(!vis[to[i]]&&to[i]!=fa){
            getG(to[i],p,sum);//这个时候既可以得出G,又可以得出下面的siz
            siz[p]+=siz[to[i]];
            maxn[p]=max(maxn[p],siz[to[i]]);//找出最大子树
        }
    }
    maxn[p]=max(maxn[p],sum-siz[p]);//当然还要这棵树的上半部分比较
    if(maxn[G]>maxn[p])G=p;
}

int dep[N],num,len[N];
void dfs(int p,int fa){
    for(int i=head[p];~i;i=nex[i]){
        if(vis[to[i]]||fa==to[i])continue;
        len[++num]=dep[to[i]]=dep[p]+v[i];
        dfs(to[i],p);
    }
}
int cal(int p,int length){//重点,多加的部分的消除
    int sum=0;
    num=0;
    len[++num]=dep[p]=length;//自己也加进去
    dfs(p,0);//得出离G点距离
    sort(len+1,len+1+num);
    for(int l=1,r=num;r>l;){
        if(dep[r]+dep[l]<=k)sum+=r-l,l++;//r-l对可以匹配,l已经匹配完了
        else r--;//r已经不能匹配了
    }
    return sum;
}

void divide(int p){
    //printf("G==%d\n",p);
    vis[p]=1;
    ans+=cal(p,0);
    for(int i=head[p];~i;i=nex[i]){
        if(vis[to[i]])continue;
        ans-=cal(to[i],v[i]);
        G=0;//为了初始化
        maxn[0]=siz[to[i]];
        getG(to[i],0,siz[to[i]]);//一套做全都是0
        divide(G);
    }
}

int main(){
    while(cin>>n>>k){
        if(!n&&!k)break;
        ans=0;mmm(head,-1);now=0;mmm(vis,0);
        for(int i=1,a,b,v;i<n;i++)a=read(),b=read(),v=read(),add(a,b,v);
        maxn[0]=n;//因为在getG的时候可以和maxn[0]比较来初始化G(使第一个fa==0)
        G=0;getG(1,0,n);
        divide(G);
        printf("%d\n",ans);
    }

}

猜你喜欢

转载自blog.csdn.net/jk_chen_acmer/article/details/81257618