您的位置:首页 > 其它

[HZOI 2015]复仇的序幕曲

2018-01-22 08:40 274 查看

【题目描述】

你还梦不梦痛不痛,回忆这么重你怎么背得动 ----序言

当年的战火硝烟已经渐渐远去,可仇恨却在阿凯蒂王子的心中越来越深

他的叔父三年前谋权篡位,逼宫杀死了他的父王,用铁血手腕平定了国内所有的不满

只有他一个人孤身逃了出来,而现在他组织了一只强大的军队,反攻的号角已经吹响

大战一触即发,作为他的机智又勇敢的指挥官,你必须要准确及时的完成他布置的任务

这个国家的布局是一棵树,每个城市都是树上的结点,其中每个结点上都有军队ai(人数)

树上的每条边有边权wi,表示通过这条边所需要的时间

当一个城市u受到攻击时,所有城市的军队都会同时向这个城市移动

阿凯蒂王子需要知道在时间T内,u城市最多聚集多少人


【输入格式】

第一行n,m,分别表示城市数目和询问次数

第二行有n个正整数,表示每个结点军队人数ai

以下n-1行每行描述树上的一条边的两个端点u,v和边权w

以下m行每行一个询问u,T

表示在时间T内,u城市最多聚集多少人

注意询问之间相互独立


【输出格式】

输出m行,每行一个数

表示询问的答案


【样例输入】

5 5

3 7 1 7 4

2 1 9

3 1 6

4 2 5

5 3 1

5 1

4 3

1 1

1 4

4 2


【样例输出】

5

7

3

3

7


【提示】

n<=80000,m<=80000

边权和军队人数均<=1000

题解:

先简化一下题意,给你一棵树,树上每个点都有一个点权,每次询问和一个点距离小于等于T的所有点的点权和。

考虑动态点分,对于每个节点,我们保存两个vector,a[i][0]表示整棵子树到这个点的权值和,a[i][1]表示整棵子树到i的父节点的点权和。

每个vector中保存两个参数,len和x,表示距离小于等于len的点权和是多少。

然后用前缀和统计一下x,每次二分len查询即可。

每次查询一个数容斥一下就好。

//Never forget why you start
#include<iostream>
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<cmath>
#include<algorithm>
#include<vector>
#define inf (2147483647)
using namespace std;
int n,m,a[100005],lim;
struct node{
int next,to,dis;
}edge[200005];
int head[100005],size;
void putin(int from,int to,int dis){
size++;
edge[size].next=head[from];
edge[size].to=to;
edge[size].dis=dis;
head[from]=size;
}
int fa[100005][20],dis[100005],depth[100005];
void dfs1(int r,int father){
int i;
fa[r][0]=father;
depth[r]=depth[father]+1;
for(i=head[r];i!=-1;i=edge[i].next){
int y=edge[i].to;
if(y!=father){
dis[y]=dis[r]+edge[i].dis;
dfs1(y,r);
}
}
}
void make(){
lim=log(n)/log(2);
for(int i=1;i<=lim;i++)
for(int j=1;j<=n;j++)
fa[j][i]=fa[fa[j][i-1]][i-1];
}
int LCA(int x,int y){
if(depth[x]<depth[y])swap(x,y);
for(int i=lim;i>=0;i--)
if(depth[fa[x][i]]>=depth[y])
x=fa[x][i];
if(x!=y){
for(int i=lim;i>=0;i--)
if(fa[x][i]!=fa[y][i])
x=fa[x][i],y=fa[y][i];
x=fa[x][0];
y=fa[y][0];
}
return x;
}
int dist(int x,int y){
int lca=LCA(x,y);
return dis[x]+dis[y]-dis[lca]*2;
}
int vis[100005],cnt[100005],d[100005],root,tot,ff[100005];
void getroot(int r,int father){
int i;
cnt[r]=1;d[r]=0;
for(i=head[r];i!=-1;i=edge[i].next){
int y=edge[i].to;
if(!vis[y]&&y!=father){
getroot(y,r);
cnt[r]+=cnt[y];
d[r]=max(d[r],cnt[y]);
}
}
d[r]=max(d[r],tot-cnt[r]);
if(d[root]>d[r])root=r;
}
void buildtree(int r,int father){
int i,all=tot;
vis[r]=1;
ff[r]=father;
for(i=head[r];i!=-1;i=edge[i].next){
int y=edge[i].to;
if(!vis[y]){
if(cnt[y]>cnt[r])cnt[y]=all-cnt[r];tot=cnt[y];
root=0;getroot(y,r);buildtree(root,r);
}
}
}
struct Ans{
int len,x;
};
vector<Ans>ans[100005][2];
bool cmp(const Ans a,const Ans b){
return a.len<b.len;
}
int upper_bound(int x,int y,int k){
int l=0,r=ans[x][y].size()-1,cnt=0;
while(l<=r){
int mid=(l+r)>>1;
if(ans[x][y][mid].len<=k)cnt=ans[x][y][mid].x,l=mid+1;
else r=mid-1;
}
return cnt;
}
void insert(int x,int v){
int i;
ans[x][0].push_back((Ans){0,v});
for(i=x;ff[i];i=ff[i]){
int len=dist(x,ff[i]);
ans[i][1].push_back((Ans){len,v});
ans[ff[i]][0].push_back((Ans){len,v});
}
}
int find(int x,int k){
int i,ans=upper_bound(x,0,k);
for(i=x;ff[i];i=ff[i]){
int len=dist(x,ff[i]);
ans-=upper_bound(i,1,k-len);
ans+=upper_bound(ff[i],0,k-len);
}
return ans;
}
void clean(){
memset(head,-1,sizeof(head));
size=0;
}
int main(){
freopen("SS.in","r",stdin);
freopen("SS.out","w",stdout);
int i,j;
clean();
scanf("%d%d",&n,&m);
for(i=1;i<=n;i++)scanf("%d",&a[i]);
for(i=1;i<n;i++){
int u,v,l;
scanf("%d%d%d",&u,&v,&l);
putin(u,v,l);
putin(v,u,l);
}
dfs1(1,1);make();
tot=n;root=0;d[0]=inf;
getroot(1,0);buildtree(root,0);
for(i=1;i<=n;i++)insert(i,a[i]);
for(i=1;i<=n;i++){
sort(ans[i][0].begin(),ans[i][0].end(),cmp);
sort(ans[i][1].begin(),ans[i][1].end(),cmp);
}
for(i=1;i<=n;i++){
for(j=1;j<ans[i][0].size();j++)
ans[i][0][j].x+=ans[i][0][j-1].x;
for(j=1;j<ans[i][1].size();j++)
ans[i][1][j].x+=ans[i][1][j-1].x;
}
while(m--){
int x,k;
scanf("%d%d",&x,&k);
printf("%d\n",find(x,k));
}
return 0;
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: