# 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.

#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#define MAXN 10010
#define INF 0x3f3f3f3f
using namespace std;
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;
}
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 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(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(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);
}
else
break;
}
}
m = 0;
while(scanf("%d",&v))
{
if(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;
}


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