POJ - 2114,点分治

Boatherds

Boatherds Inc. is a sailing company operating in the country of Trabantustan and offering boat trips on Trabantian rivers. All the rivers originate somewhere in the mountains and on their way down to the lowlands they gradually join and finally the single resulting river flows to the sea. Moreover, the Trabantian villages are exactly at the rivers’ springs, junctions and at the mouth of the largest river. Please note that more than 2 rivers can join at a junction. However, the rivers always form a tree (with villages as vertices).

The pricing policy of the Boatherds is very simple: each segment of each river between two villages is assigned a price (the price is same in both directions), so if a tourist requests a journey between any two villages, the ticket office clerks just add the prices of the segments along the only path between the villages.

One day, a very strange tourist appeared. She told the clerks that she returns to her country on the next day and she wants to spend all the remaining money on a boat trip, so they should find a route with exactly this cost. Being just poor (ahem) businessmen, they have asked the Abacus Calculator Makers for help.

You are given a description of the river network with costs of river segments and a sequence of integers x1,…, xk. For each xi, you should determine if there is a pair of cities (a, b) in the river network such that the cost of the trip between a and b is exactly xi.

除了输入方式不同,其他和P3806同,详解见https://blog.csdn.net/xing_mo/article/details/104005955

#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#define MAXN 10010
#define INF 0x3f3f3f3f
using namespace std;
int head[MAXN],tot;
struct edge
{
    int v,w,nxt;
}edg[MAXN << 1];
inline void addedg(int u,int v,int w)
{
    edg[tot].v = v;
    edg[tot].w = w;
    edg[tot].nxt = head[u];
    head[u] = tot++;
}
int n,root,ms,mson[MAXN],sz[MAXN],Size;
bool vis[MAXN];
//root用于标记重心,ms表示树的重心的最大子树的大小,mson[i]记录以i为根最大子树的大小
//sz[i]记录以i为根子树的大小,Size表示当前整棵树的大小,vis[i]表示当前节点是否被分治过
void getroot(int u,int f)//获得重心
{
    sz[u] = 1,mson[u] = 0;
    int v;
    for(int i = head[u];i != -1;i = edg[i].nxt)
    {
        v = edg[i].v;
        if(vis[v] || v == f) continue;//剔除已经被分治过的点
        getroot(v,u);
        sz[u] += sz[v];
        if(sz[v] > mson[u]) mson[u] = sz[v];
    }
    if(Size - sz[u] > mson[u]) mson[u] = Size-sz[u];//把u看作根节点时u的父亲那一部分也算作子树
    if(ms > mson[u]) ms = mson[u],root = u;//更新重心
}
int m,ask[110],ans[110];//所有询问
int dis[MAXN],cnt;//dis记录所有节点到重心的距离
struct node
{
    int d,num;
}nod[MAXN];//记录距离d出现的次数num
int cc;//不同距离数
void getdis(int u,int f,int d)//获得到目标点的距离
{
    dis[++cnt] = d;
    int v;
    for(int i = head[u];i != -1;i = edg[i].nxt)
    {
        v = edg[i].v;
        if(vis[v] || v == f) continue;
        getdis(v,u,d + edg[i].w);
    }
}
void cal(int u,int d,int tp)//u表示getdis的起点,d表示u到目标点的距离,tp表示这一次统计出来的答案是合理的还是不合理的
{
    cnt = 0;
    getdis(u,0,d);//算出树中的点到目标点的距离
    sort(dis+1,dis+cnt+1);
    cc = 1,nod[cc].d = dis[1],nod[cc].num = 1;
    for(int i = 2;i <= cnt;++i)
        if(dis[i] != dis[i-1]) nod[++cc].d = dis[i],nod[cc].num = 1;
        else ++nod[cc].num;
    for(int i = 1;i <= m;++i)
    {
        int l = 1,r = cc;
        while(l <= r)
        {
            if(nod[l].d + nod[r].d == ask[i])
            {
                if(l == r) ans[i] += nod[l].num*(nod[l].num-1)/2 * tp;
                else ans[i] += nod[l].num * nod[r].num * tp;
                ++l,--r;
            }
            else if(nod[l].d + nod[r].d < ask[i])
                ++l;
            else
                --r;
        }
    }
}
void solve(int u,int ssize)//ssize是当前这棵子树的大小
{
    vis[u] = true;//代码保证每次进来的u都必定是当前这棵树的重心,我们将vis[u]标记为true,表示u点被分治过
    cal(u,0,1);//计算这棵树以u为重心的所有组合,但包括了共用同一条边的情况
    int v;
    for(int i = head[u];i != -1;i = edg[i].nxt)
    {
        v = edg[i].v;
        if(vis[v]) continue;
        cal(v,edg[i].w,-1);//将共用一条边的不合法情况去除
        ms = INF;//记得每次都要初始化
        Size = sz[v] < sz[u]?sz[v]:(ssize-sz[u]);//因为v实际上可能是u的父亲,故sz需相减
        getroot(v,v);//求出以v为根节点的子树重心
        solve(root,Size);
    }
}
inline void init()
{
    tot = 0,ms = INF,Size = n;
    memset(head,-1,sizeof(int)*(n+1));
    memset(vis,false,sizeof(bool)*(n+1));
}
int main()
{
    while(~scanf("%d",&n) && n)
    {
        init();
        int v,w;
        for(int u = 1;u <= n;++u)
        {
            while(scanf("%d",&v))
            {
                if(v)
                {
                    scanf("%d",&w);
                    addedg(u,v,w),addedg(v,u,w);
                }
                else
                    break;
            }
        }
        m = 0;
        while(scanf("%d",&v))
        {
            if(v)
                ask[++m] = v;
            else
                break;
        }
        memset(ans,0,sizeof(int)*(m+1));
        getroot(1,1);
        solve(root,Size);
        for(int i = 1;i <= m;++i)
            if(ans[i] > 0) printf("AYE\n");
            else printf("NAY\n");
        puts(".");
    }
    return 0;
}
发布了47 篇原创文章 · 获赞 1 · 访问量 2612
展开阅读全文

没有更多推荐了,返回首页

©️2019 CSDN 皮肤主题: 技术黑板 设计师: CSDN官方博客

分享到微信朋友圈

×

扫一扫,手机浏览