poj2114 Boatherds

来源:互联网 发布:杭州淘宝大学培训机构 编辑:程序博客网 时间:2024/06/12 04:53

判断树上是否存在长度为k的路径

关键词:点分治、等值特判、扫描法

判断方法仍为扫描法,只不过相等的时候需要特判,否则会错过许多条k长路径

#include<stdio.h>#include<iostream>#include<algorithm>#include<string.h>#define maxn 20010#define lowbit(x) x&(-x)#define mem(a,b) memset(a,b,sizeof(a))#define ll long long#define INF 0x3f3f3f3fusing namespace std;int n,m;int k;struct Edge{    int to,next;    ll w;}edge[maxn*2];int head[maxn],tot;ll sum,f[maxn],vis[maxn],son[maxn],core,d[maxn];//getcorell ans,deep[maxn];void init(){    mem(head,-1),tot=0;}void add(int a,int b,ll c){    edge[tot].to=b,edge[tot].next=head[a],edge[tot].w=c;    head[a]=tot++;}void getcore(int u,int fa){    son[u]=1,f[u]=0;//每次更新前必须初始化!!!---dfs可以在搜索子树前在内部对点初始化    for(int i=head[u];i!=-1;i=edge[i].next){        int v=edge[i].to;        if(v==fa||vis[v]) continue;//除去已找到的重心后求子树重心        getcore(v,u);        son[u]+=son[v],f[u]=max(f[u],son[v]);    }    f[u]=max(f[u],sum-son[u]);    if(f[u]<f[core]) core=u;//core记录该树重心}void getdeep(int u,int fa){//得到u子树每个节点的深度    deep[++deep[0]]=d[u];    for(int i=head[u];i!=-1;i=edge[i].next){        int v=edge[i].to; ll w=edge[i].w;        if(v==fa||vis[v]) continue;        d[v]=d[u]+w;        getdeep(v,u);    }}ll cal(int u,int w){//1.该点是重心:求该点子树每点到该点深度2.该点父节点是重心:求该点子树到父节点的深度    deep[0]=0,d[u]=w; getdeep(u,0);    sort(deep+1,deep+deep[0]+1);    int tmp=0;    for(int l=1,r=deep[0];l<r;){        if(deep[l]+deep[r]<k)  l++;        else if(deep[l]+deep[r]>k) r--;        else{//计数出错!!!---可能出现相同的数,此时应该特殊处理!!!            if(deep[l]==deep[r]){                tmp+=((r-l)*(r-l+1)/2);                break;            }            int st=l,en=r;            while(deep[st]==deep[l]) st++;            while(deep[en]==deep[r]) en--;            tmp+=(st-l)*(r-en);            l=st,r=en;        }    }    return tmp;}void work(int u){    ans+=cal(u,0);//经过原树重心的k路径条数    vis[u]=1;//原树重心已算,标记    for(int i=head[u];i!=-1;i=edge[i].next){        int v=edge[i].to,w=edge[i].w;        if(vis[v]) continue;//如果v已经作为重心枚举过,u不会遍历到v,因此不必除去        ans-=cal(v,w);//除去经过重心同一子树的路径        sum=son[v];//更改当前树节点总和        core=0;        getcore(v,core);        work(core);    }}int main(){    //freopen("a.txt","r",stdin);    while(scanf("%d",&n)!=EOF){        if(!n) break;        init();        for(int i=1;i<=n;i++){            int a;            ll b;            while(scanf("%d",&a)){                if(!a) break;                scanf("%lld",&b);                add(a,i,b),add(i,a,b);            }        }        while(scanf("%d",&k)){            if(!k) break;            mem(vis,0),ans=0;            core=0,sum=n,f[0]=INF;            getcore(1,0);            work(core);            if(ans) printf("AYE\n");            else printf("NAY\n");        }        printf(".\n");    }    return 0;}
bug:等值特判

0 0