poj 2114 树分治(是否存在长度恰为k的路径)

题意:给定一棵树,随后给出不多于100个查询k,问树上是否存在恰等于k的路径。

思路:对每一个k做一遍树分治,与1714的区别就是update函数的写法,此题要求的是路径距离恰等于k的条数。(其中判断条件j>i没加到时tle了两天,苦苦debug才AC)。

#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#define clr(s,t) memset(s,t,sizeof(s))
#define max(a,b) ((a)>(b)?(a):(b))
#define min(a,b) ((a)<(b)?(a):(b))
#define N 10005
struct edge{
    int y,w,next;
}e[N<<1];
int first[N],used[N],son[N],tnum[N],q[N],dis[N];
int res;
int top,n,m,k,len,size,root;
void init(){
    res = len = 0;
    clr(used, 0);
    tnum[0] = N;
}
void add(int x,int y,int w){
    e[top].y = y;
    e[top].w = w;
    e[top].next = first[x];
    first[x] = top++;
}
int cmp(const void* a,const void* b){
    return (*(int*)a) - (*(int*)b);
}
void getroot(int x,int fa){
    int i,y;
    son[x] = 1;
    tnum[x] = 0;
    for(i = first[x];i!=-1;i=e[i].next){
        y = e[i].y;
        if(!used[y] && y!=fa){
            getroot(y, x);
            son[x] += son[y];
            tnum[x] = max(tnum[x], son[y]);
        }
    }
    tnum[x] = max(tnum[x], size-son[x]);
    if(tnum[x] < tnum[root])
        root = x;
}
void getdis(int x,int fa){
    int i,y;
    q[len++] = dis[x];
    son[x] = 1;
    for(i = first[x];i!=-1;i=e[i].next){
        y = e[i].y;
        if(!used[y] && y!=fa){
            dis[y] = dis[x]+e[i].w;
            getdis(y, x);
            son[x] += son[y];
        }
    }
}
int update(){
    int i,j,a,b,sum=0;
    qsort(q, len, sizeof(int), cmp);
    for(i = 0,j = len-1;;i++){
        while(j>i&&q[i]+q[j]>k)//没有加j>i这句tle了无数次
            j--;
        if(i>=j)
            break;
        if(q[i]+q[j]<k)
            continue;
        if(q[i] == q[j]){
            sum += (j-i)*(j-i+1)/2;
            break;
        }
        a = i,b = j;
        while(q[a] == q[i])
            a++;
        while(q[b] == q[j])
            b--;
        sum += (a-i)*(j-b);
        i = a-1;
        j = b;
    }
    return sum;
}
void tdc(int x){
    int i,y;
    getroot(x,root=0);
    dis[root] = len = 0;
    getdis(root,0);
    res += update();
    used[root] = 1;
    for(i = first[root];i!=-1;i=e[i].next){
        y = e[i].y;
        if(!used[y]){
            len = 0;
            dis[y] = e[i].w;
            getdis(y, root);
            res -= update();//减去root上的长度为k的路径而实际上这两个点在一课子树中(即含有重复边)
            size = son[y];
            tdc(y);
        }
    }
}
int main(){
    while(scanf("%d",&n) && n){
        int i,j,w;
        clr(first, -1);
        top = 0;
        for(i = 1;i<=n;i++)
            while(scanf("%d",&j) && j){
                scanf("%d",&w);
                add(i,j,w);
                add(j,i,w);
            }
        while(scanf("%d",&k) && k){
            init();
            size = n;
            tdc(1);
            if(res)
                printf("AYE\n");
            else
                printf("NAY\n");
        }
        printf(".\n");
    }
    return 0;
}


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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值