BZOJ4381: [POI2015]Odwiedziny

Description

给定一棵n个点的树,树上每条边的长度都为1,第i个点的权值为a[i]。
Byteasar想要走遍这整棵树,他会按照某个1到n的全排列b走n-1次,第i次他会从b[i]点走到b[i+1]点,并且这一次的步伐大小为c[i]。
对于一次行走,假设起点为x,终点为y,步伐为k,那么Byteasar会从x开始,每步往前走k步,如果最后不足k步就能到达y,那么他会一步走到y。
请帮助Byteasar统计出每一次行走时经过的所有点的权值和。

Input

第一行包含一个正整数n(2<=n<=50000)。表示节点的个数。
第二行包含n个正整数,其中第i个数为a[i](1<=a[i]<=10000),分别表示每个点的权值。
接下来n-1行,每行包含两个正整数u,v(1<=u,v<=n),表示u与v之间有一条边。
接下来一行包含n个互不相同的正整数,其中第i个数为b[i](1<=b[i]<=n),表示行走路线。
接下来一行包含n-1个正整数,其中第i个数为c[i](1<=c[i]<n),表示每次行走的步伐大小。

Output

包含n-1行,每行一个正整数,依次输出每次行走时经过的所有点的权值和

Sample Input

5
1 2 3 4 5
1 2
2 3
3 4
3 5
4 1 5 2 3
1 3 1 1

Sample Output

10
6
10
5
 
对于k比较大的情况,直接暴力。
对于k比较小的情况,我们一起计算。
具体实现看code
#include<cstdio>
#include<cctype>
#include<queue>
#include<cmath>
#include<cstring>
#include<algorithm>
#define rep(i,s,t) for(int i=s;i<=t;i++)
#define dwn(i,s,t) for(int i=s;i>=t;i--)
#define ren for(int i=first[x];i;i=next[i])
using namespace std;
inline int read() {
    int x=0,f=1;char c=getchar();
    for(;!isdigit(c);c=getchar()) if(c=='-') f=-1;
    for(;isdigit(c);c=getchar()) x=x*10+c-'0';
    return x*f;
}
const int maxn=50010;
int n,val[maxn],b[maxn],c[maxn],first[maxn],next[maxn<<1],to[maxn<<1],e;
void AddEdge(int u,int v) {
    to[++e]=v;next[e]=first[u];first[u]=e;
    to[++e]=u;next[e]=first[v];first[v]=e;
}
int dep[maxn],anc[maxn][20];
void dfs(int x,int fa) {
    dep[x]=dep[fa]+1;anc[x][0]=fa;
    rep(i,1,19) anc[x][i]=anc[anc[x][i-1]][i-1];
    ren if(to[i]!=fa) dfs(to[i],x);
}
int go(int x,int k) {
    rep(i,0,19) if(k>>i&1) x=anc[x][i];
    return x;
}
int lca(int x,int y) {
    if(dep[x]<dep[y]) swap(x,y);
    dwn(i,19,0) if(1<<i<=dep[x]-dep[y]) x=anc[x][i];
    dwn(i,19,0) if(anc[x][i]!=anc[y][i]) x=anc[x][i],y=anc[y][i];
    return x==y?x:anc[x][0];
}
int vis[maxn],clo;
void add(int& ans,int x) {
    if(vis[x]!=clo) ans+=val[x];
    vis[x]=clo;
}
int solve(int x,int y,int k) {
    int z=lca(x,y),len=dep[x]+dep[y]-dep[z]*2,ans=0;clo++;
    while(1) {
        add(ans,x);int t=go(x,k);
        if(dep[t]>=dep[z]) x=t;
        else break;
    }
    add(ans,y);y=go(y,len%k);
    while(dep[y]>dep[z]) {
        add(ans,y);int t=go(y,k);
        if(dep[t]>dep[z]) y=t;
        else break;
    }
    return ans;
}
int ans[maxn],first2[maxn],id[maxn],next2[maxn],cnt;
void AddQuery(int c,int i) {
    id[++cnt]=i;next2[cnt]=first2[c];first2[c]=cnt;
}
int dis[maxn];
void dfs2(int x,int fa,int gap) {
    dis[x]=val[x];if(dep[x]>gap) dis[x]+=dis[go(x,gap)];
    ren if(to[i]!=fa) dfs2(to[i],x,gap);
}
int main() {
    n=read();rep(i,1,n) val[i]=read();
    rep(i,2,n) AddEdge(read(),read());
    dfs(1,0);int SIZE=10;
    rep(i,1,n) b[i]=read();
    rep(i,2,n) {
        c[i]=read();
        if(c[i]>SIZE) ans[i]=solve(b[i-1],b[i],c[i]);
        else AddQuery(c[i],i);
    }
    rep(i,1,SIZE) if(first2[i]) {
        dfs2(1,0,i);
        for(int j=first2[i];j;j=next2[j]) {
            int u=b[id[j]-1],v=b[id[j]],z=lca(u,v);
            int len=dep[u]+dep[v]-2*dep[z],x=go(u,(dep[u]-dep[z])/i*i);
            ans[id[j]]+=dis[u]-dis[x]+val[x];
            int y=go(v,len%i),w=go(y,(dep[y]-dep[z])/i*i);
            if(y!=v) ans[id[j]]+=val[v];
            ans[id[j]]+=dis[y]-dis[w]+val[w];
            if(x==w) ans[id[j]]-=val[w];
        }
    }
    rep(i,2,n) printf("%d\n",ans[i]);
    return ~~(0-0);
}
View Code

 

posted @ 2016-01-16 16:05  wzj_is_a_juruo  阅读(354)  评论(0编辑  收藏  举报