1. 程式人生 > >Odwiedziny[POI 2015]

Odwiedziny[POI 2015]

ans 計算 math 並且 其中 tel main 大小 所有

題目描述

給定一棵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統計出每一次行走時經過的所有點的權值和。

輸入

  • Line 1:一個正整數n(2<=n<=50000)。表示節點的個數。
  • Line 2:n個正整數,其中第i個數為ai(1ai10000)
    ,分別表示每個點的權值。
  • Line 3~n+1:包含兩個正整數u,v(1u,vn),表示u與v之間有一條邊。
  • Line n+2:n個互不相同的正整數,其中第i個數為b[i](1b[i]n),表示行走路線。
  • Line n+3:n-1個正整數,其中第i個數為ci(1ci<n),表示每次行走的步伐大小。

輸出

包含n-1行,每行一個正整數,依次輸出每次行走時經過的所有點的權值和

SOL:

我們觀察了一波局勢,發現這道題有些眼熟。相比大家應該都做過這樣一道題:給你一個靜態的序列(所謂靜態是指這個區間不會做任何的修改),M次給出X,L,K,讓你求從X起的L個數的和,這L個數中間兩兩下表相間K(舉個栗子:X=5,L=4,K=3,那麽sum=a5+a8+a11+a14)。(N<=10W,M<=10W)

這道題我們的思想就是分塊,我們發現當K很大時,我們暴力求和的速度是相當快的。因為我們發現 L<=N/K,我們的暴力復雜度是O(L)的。而K很小時,暴力就很慢了,接近於O(N)。我們又註意到K=1時便是前綴和,可以做到O(1)查詢。那麽我們不禁想當K很小時我們用前綴和優化(K>1有間距的前綴和相比大家都會)。

我們假設我們采取這樣的策略,當K<S時我們用前綴和,K>S時我們采取暴力,那麽我們就可以在最壞情況下O(n*s+m*n/s)下解決問題,那麽我們解得S=sqrt N時最優。(實際上S取的稍微小一些會更快,因為我們剛剛計算的是最壞情況)

那麽我們就可以做這一道題了,同樣的思想,當K<sqrt N 時我們預處理,K > sqrt N 時我們便暴力向上跳。

case 1: K<sqrt N 我們預處理每個點 以K為間距向上跳的 答案,x的答案加上y的答案減去lca的答案就是了。

case 1: K>sqrt N 我們暴力做,我們每次用樹上倍增的方法 log N 的方法向上跳,暴力統計答案,跳跳就好了。

看代碼:

#include<bits/stdc++.h>
#define sight(c) (‘0‘<=c&&c<=‘9‘)
#define eho(x) for (int i=head[x];i;i=net[i])
#define N 50007
#define BRE 237
#define SIZ 21
int a[N],fall[N<<1],net[N<<1],head[N],tot,f[SIZ][N],dep[N],val[N][BRE],g[BRE][N],
m,rog,ot,r,ans,n,x,y,len[N],k;
inline void read(int &x){
    static char c;
    for (c=getchar();!sight(c);c=getchar());
    for (x=0;sight(c);c=getchar()) x=x*10+c-48;
}
using namespace std;
inline void add(int x,int y){fall[++tot]=y; net[tot]=head[x]; head[x]=tot;}
void dfs(int x,int fa){
   f[0][x]=fa;dep[x]=dep[fa]+1;
    eho(x) if(fall[i]^fa)  dfs(fall[i],x);
}
void Dfs(int x){
   for (int i=1;i<=m;i++) val[x][i]=val[g[i][x]][i]+a[x];
   eho(x) if (fall[i]^f[0][x]) Dfs(fall[i]);
}
void write(int x){if (x<10) {putchar(0+x); return;} write(x/10); putchar(0+x%10);}
inline void writeln(int x){ if (x<0) putchar(-),x*=-1; write(x); putchar(\n); }
inline int lca(int x,int y){
    if (dep[x]<dep[y]) swap(x,y);
    for (int i=SIZ-1;~i;i--) if (dep[f[i][x]]>=dep[y]) x=f[i][x];
    if (x==y) return x;
    for (int i=SIZ-1;~i;i--) if (f[i][x]^f[i][y]) x=f[i][x],y=f[i][y];
    return f[0][x];
}
inline int up(int x,int k) {if (k<=m)return g[k][x];for (int i=SIZ-1;~i;i--) if ((k>>i)&1) x=f[i][x];return x;}
inline int rop(int x,int y,int k) {
    if (dep[x]<dep[y]) return 0;rog=0;
    if (k<m) {
        rog+=val[x][k]; ot=k-(dep[x]-dep[y])%k; rog-=val[k==ot?y:up(y,ot%k)][k];
    } else while (dep[x]>dep[y]) rog+=a[x],x=up(x,k);
    return rog;
}
void solve(int x,int y,int k){
    int L=lca(x,y); r=(dep[x]+dep[y]-(dep[L]<<1))%k;
    ans=rop(x,L,k);
    if (r) ans+=a[y],y=up(y,k);
    ans+=rop(y,f[0][L],k);
    writeln(ans);
}
int main () {
    freopen("a.in","r",stdin);
    read(n); m=min((int)sqrt(n),BRE-2);
    for (int i=1;i<=n;i++) read(a[i]),g[0][i]=a[i];
    for (int i=n-1;i;i--) read(x),read(y),add(x,y),add(y,x);
    dfs(1,0);
    for (int j=1;j<SIZ;j++)
     for (int i=1;i<=n;i++) f[j][i]=f[j-1][f[j-1][i]];
    memcpy(g[1],f[0],sizeof f[0]);
    for (int j=2;j<=m;j++)
     for (int i=1;i<=n;i++) g[j][i]=g[1][g[j-1][i]];
    Dfs(1);
    for (int i=1;i<=n;i++) read(len[i]);
    for (int i=1;i<n;i++) read(k),
     solve(len[i],len[i+1],k);
    return 0;
}

Odwiedziny[POI 2015]