POJ 2114 Boatherds

POJ 2114 Boatherds

点分治

题意

给一棵带边权的树,给一系列询问,问是否存在一条路径,使得路上权值和恰好为K。

思路

点分治。
100个询问,每次做一遍点分治。点分治记录所有路径长度,在二分查找是否有满足条件的长度。

代码

#include<cstdio>
#include<cstring>
#include<cstdlib>
#include<algorithm>
using namespace std;
const int MAXN=20005;
struct Edge
{
    int to, ne;
    int w;
}e[MAXN*2+7];
int edgenum, head[MAXN];
void addedge(int from, int to, int w)
{
    e[edgenum].to=to, e[edgenum].w=w, e[edgenum].ne=head[from], head[from]=edgenum++;
    e[edgenum].to=from, e[edgenum].w=w, e[edgenum].ne=head[to], head[to]=edgenum++;
}
int sz[MAXN], mx[MAXN], root, vis[MAXN];
void getrt(int x, int f, int n)//找x的子树内的重心root
{
    sz[x]=1;mx[x]=0;
    for(int i=head[x];~i;i=e[i].ne)
    {
        int to=e[i].to;
        if(to==f||vis[to]) continue;
        getrt(to, x, n);
        sz[x]+=sz[to];
        mx[x]=max(mx[x], sz[to]);
    }
    mx[x]=max(mx[x], n-sz[x]);
    if(mx[x]<mx[root]) root=x;
}
int dis[MAXN], discnt;
int d[MAXN];
void getdis(int x, int f)
{
    dis[discnt++]=d[x];
    for(int i=head[x];~i;i=e[i].ne)
    {
        int to=e[i].to;
        if(to==f||vis[to]) continue;
        d[to]=(d[x]+e[i].w);
        getdis(to, x);
    }
}
int calc(int x, int nowd, int k)
{
    discnt=0;
    d[x]=nowd;
    getdis(x, -1);
    int cnt=0;
    sort(dis, dis+discnt);
    for(int i=0;i<discnt;i++)
    {
        if(dis[i]+dis[i]>k) break;
        int yu=k-dis[i];

        int l=i+1, r=discnt-1;
        int resl=0, resr=-1;
        while(l<=r)
        {
            int mid=(l+r)>>1;
            if(dis[mid]==yu) r=mid-1, resl=mid;
            else if(dis[mid]<yu) l=mid+1;
            else r=mid-1;
        }

        l=i+1, r=discnt-1;
        while(l<=r)
        {
            int mid=(l+r)>>1;
            if(dis[mid]==yu) l=mid+1, resr=mid;
            else if(dis[mid]<yu) l=mid+1;
            else r=mid-1;
        }

        cnt+=(resr-resl+1);
    }
    return cnt;
}
int ans;
void work(int x, int k)
{
    vis[x]=1;
    ans+=calc(x, 0, k);
    for(int i=head[x];~i;i=e[i].ne)
    {
        if(vis[e[i].to]) continue;
        ans-=calc(e[i].to, e[i].w, k);
        root=0;
        getrt(e[i].to, -1, sz[e[i].to]);
        work(root, k);
    }
}
int main()
{
    int n=0;
    while(scanf("%d", &n)==1&&n)
    {
        memset(head, -1, sizeof(head));
        edgenum=0;
        for(int i=1;i<=n;i++)
        {
            int a, b;
            while(scanf("%d", &a)==1&&a)
            {
                scanf("%d", &b);
                addedge(i, a, b);
            }
        }
        int q, qcnt=0;
        while(scanf("%d", &q)==1&&q)
        {
            memset(vis, 0, sizeof(vis));
            discnt=0;
            mx[0]=0x3f3f3f3f;ans=0;root=0;
            getrt(1, -1, n);
            work(root, q);
            //printf("%d\n", ans);
            printf("%s\n", ans>0 ? "AYE" : "NAY");
        }
        puts(".");
    }
    return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值