Happy King
Time Limit: 10000/5000 MS (Java/Others) Memory Limit: 262144/262144 K (Java/Others)
Total Submission(s): 1077 Accepted Submission(s): 265
Problem Description
There are n cities and n−1 roads in Byteland, and they form a tree. The cities are numbered 1 through n. The population in i-th city is pi.
Soda, the king of Byteland, wants to travel from a city u to another city v along the shortest path. Soda would be happy if the difference between the maximum and minimum population among the cities passed is **no larger than** D. So, your task is to tell Soda how many different pairs (u,v) that can make him happy.
Input
There are multiple test cases. The first line of input contains an integer T (1≤T≤500), indicating the number of test cases. For each test case:
The first line contains two integers n and D (1≤n≤100000,1≤D≤109).
The second line contains n integers p1,p2,…,pn (0≤pi≤109).
Each of the following n−1 lines describing roads contains two integers u,v (1≤u,v≤n,u≠v) meaning that there is a road connecting city u and city v.
It is guaranteed that the total number of vertices in the input doesn't exceed 5×105.
Output
For each test case, output the number of different pairs that can make Soda happy.
Sample Input
1 3 3 1 2 3 1 2 2 3
Sample Output
6
题意:
第一个t,测试数据
给你n个点,每个点有权值pi,再给你n-1条边,问你有多少对(u,v)使得,u->v的最短路径上经过的点的最大权值与最小权值的差<=k
解析:
实际上就是树的点分治。
不过这里的条件是路径上经过的最大权值和最小权值。
找重心的代码就说了。
关键是在一个子树中,找到当前子树的根节点到子树中各个节点路径上的最小值和最大值,然后如果最大值-最小值<=k,那么保存在pp[]里
之后,将pp按最小值排序,这样从前往后扫一遍就可以得出,经过当前子树根节点且满足条件的(u,v)的个数
因为这里当你用Pp[i].max去找时满足条件的节点Pp[j].min(j<i)时,
Pp[i].max-Pp[i].min<=k,
pp[j].max-pp[j].min<=k
pp[j].min<=pp[i].min
=>pp[j].max<=k+pp[j].min<=pp[i].min+k
所以只要构造pp[i].max-pp[j].min<=k,那么这两个点无论如何(i点的max大,还是j点的max大),都能满足条件
#include <cstdio>
#include <algorithm>
#include <cstring>
using namespace std;
typedef long long int lli;
#define INF 9999999999
typedef struct node
{
int u,v;
int nxt;
}node;
typedef struct point
{
lli _min,_max;
bool operator < (const point& b)
{
return _min==b._min?_max<b._max:_min<b._min;
}
}point;
const int MAXN = 1e5+110;
point pp[MAXN];
int n,cnt,ck,ncount;
lli k;
lli ver[MAXN];
int head[MAXN];
node edge[2*MAXN];
int visit[MAXN],siz[MAXN],maxf[MAXN];
lli ans;
void addedge(int u,int v)
{
edge[cnt].u=u;
edge[cnt].v=v;
edge[cnt].nxt=head[u];
head[u]=cnt++;
}
int wr;
void getroot(int u,int fa)
{
siz[u]=1;
maxf[u]=0;
for(int i=head[u];i!=-1;i=edge[i].nxt)
{
int v=edge[i].v;
if(v!=fa&&!visit[v])
{
getroot(v,u);
maxf[u]=max(maxf[u],siz[v]);
siz[u]+=siz[v];
}
}
maxf[u]=max(maxf[u],ncount-siz[u]); //Count表示当前子树的结点的总个数
if(maxf[u]<maxf[wr]) wr=u;
}
void dfs_maxmin(int u,int fa,lli maxv,lli minv) //这里还需要重新计算每个子树的size
{ 算出从根节点到每一个节点的路径中的最大值和最小值
maxv=max(maxv,ver[u]);
minv=min(minv,ver[u]);
if(maxv-minv<=k)
{
pp[ck]=point{minv,maxv};
ck++;
}
for(int i=head[u];i!=-1;i=edge[i].nxt)
{
int v=edge[i].v;
if(v!=fa&&!visit[v])
{
dfs_maxmin(v,u,maxv,minv);
}
}
}
lli cal(int u,int _min,int _max)
{
lli ret=0;
ck=0;
dfs_maxmin(u,-1,_max,_min);
sort(pp,pp+ck); //P+i限制了查找答案只能在比P[i].min小的点里面找,那么无论是P[i].max大还是P[j].max大,最后他与最小值的差p[j].min都小于等于k(j在[1,i)满足条件的节点)
for(int i=ck-1;i>=0;i--)
{
int num=lower_bound(pp,pp+i,point{pp[i]._max-k,0})-pp;
ret+=i-num; //num为最远的满足条件的下标,在[num,i)之间的值都满足条件
}
return ret;
}
void DFS(int u)
{
maxf[0]=ncount=siz[u];
wr=0;
getroot(u,-1);
int root=wr;
visit[root]=1;
ans+=cal(root,INF,-INF);
for(int i=head[root];i!=-1;i=edge[i].nxt)
{
int v=edge[i].v;
if(!visit[v])
{ //保持与前面情况一样,只是节点只有的左(右)子树的点,这样就可以找出一边的重复情况
ans-=cal(v,ver[root],ver[root]);
DFS(v);
}
}
}
int main()
{
int t;
scanf("%d",&t);
while(t--)
{
ans=0;
scanf("%d%lld",&n,&k);
for(int i=1;i<=n;i++)
scanf("%lld",&ver[i]);
cnt=0;
memset(head,-1,sizeof(head));
for(int i=1;i<n;i++)
{
int u,v;
scanf("%d%d",&u,&v);
addedge(u,v);
addedge(v,u);
}
memset(visit,0,sizeof(visit));
siz[1]=n;
DFS(1);
printf("%lld\n",ans*2);
}
}