1. 程式人生 > >BZOJ4012[HNOI2015]開店——樹鏈剖分+可持久化線段樹

BZOJ4012[HNOI2015]開店——樹鏈剖分+可持久化線段樹

頂點 一個地方 find tar include lower ans 輸出 安靜

題目描述

風見幽香有一個好朋友叫八雲紫,她們經常一起看星星看月亮從詩詞歌賦談到

人生哲學。最近她們靈機一動,打算在幻想鄉開一家小店來做生意賺點錢。這樣的 想法當然非常好啦,但是她們也發現她們面臨著一個問題,那就是店開在哪裏,面 向什麽樣的人群。很神奇的是,幻想鄉的地圖是一個樹形結構,幻想鄉一共有 n 個地方,編號為 1 到 n,被 n-1 條帶權的邊連接起來。每個地方都住著一個妖怪, 其中第 i 個地方的妖怪年齡是 x_i。妖怪都是些比較喜歡安靜的家夥,所以它們並 不希望和很多妖怪相鄰。所以這個樹所有頂點的度數都小於或等於 3。妖怪和人一 樣,興趣點隨著年齡的變化自然就會變化,比如我們的 18 歲少女幽香和八雲紫就 比較喜歡可愛的東西。幽香通過研究發現,基本上妖怪的興趣只跟年齡有關,所以 幽香打算選擇一個地方 u(u為編號),然後在 u開一家面向年齡在 L到R 之間(即 年齡大於等於 L、小於等於 R)的妖怪的店。也有可能 u這個地方離這些妖怪比較 遠,於是幽香就想要知道所有年齡在 L 到 R 之間的妖怪,到點 u 的距離的和是多 少(妖怪到 u 的距離是該妖怪所在地方到 u 的路徑上的邊的權之和) ,幽香把這個 稱為這個開店方案的方便值。幽香她們還沒有決定要把店開在哪裏,八雲紫倒是準 備了很多方案,於是幽香想要知道,對於每個方案,方便值是多少呢。

輸入

第一行三個用空格分開的數 n、Q和A,表示樹的大小、開店的方案個數和妖

怪的年齡上限。 第二行n個用空格分開的數 x_1、x_2、…、x_n,x_i 表示第i 個地點妖怪的年 齡,滿足0<=x_i<A。(年齡是可以為 0的,例如剛出生的妖怪的年齡為 0。) 接下來 n-1 行,每行三個用空格分開的數 a、b、c,表示樹上的頂點 a 和 b 之 間有一條權為c(1 <= c <= 1000)的邊,a和b 是頂點編號。 接下來Q行,每行三個用空格分開的數 u、 a、 b。對於這 Q行的每一行,用 a、 b、A計算出 L和R,表示詢問“在地方 u開店,面向妖怪的年齡區間為[L,R]的方 案的方便值是多少”。對於其中第 1 行,L 和 R 的計算方法為:L=min(a%A,b%A), R=max(a%A,b%A)。對於第 2到第 Q行,假設前一行得到的方便值為 ans,那麽當 前行的 L 和 R 計算方法為: L=min((a+ans)%A,(b+ans)%A), R=max((a+ans)%A,(b+ans)%A)。

輸出

對於每個方案,輸出一行表示方便值。

樣例輸入

10 10 10
0 0 7 2 1 4 7 7 7 9
1 2 270
2 3 217
1 4 326
2 5 361
4 6 116
3 7 38
1 8 800
6 9 210
7 10 278
8 9 8
2 8 0
9 3 1
8 0 8
4 2 7
9 7 3
4 7 0
2 2 7
3 2 1
2 3 4

樣例輸出

1603
957
7161
9466
3232
5223
1879
1669
1282
0

提示

滿足 n<=150000,Q<=200000。對於所有數據,滿足 A<=10^9

  這道題和BZOJ3626比較相似.首先考慮沒有年齡限制,就是求一些點和一個點x的距離和.答案就是Σdep[i]+dep[x]-2*dep[lca],dep[i]可以處理前綴和,再求出點數乘上dep[x]就就得到了前半部分答案,最後lca深度和怎麽求?如果單獨求兩點x,y的lca深度可以將x到根路徑上所有邊權值+1(每條邊權值相同情況下)然後再求y到根路徑上邊權和就好了。那麽一些點和x的lca深度和也可以用同樣的求法,按樹剖序建線段樹,邊權下傳到點上,每次跳重鏈修改和查詢。因為這道題邊權不同,所以每次給邊權+1表示這條邊對答案貢獻次數+1,維護一個永久化的標記(就是不下傳的標記)表示區間要對答案貢獻幾次,再每個點維護一個貢獻和表示這個點代表的區間對答案的總貢獻(即這個點在線段樹中子樹中所有點的永久化標記*區間權值和之和)就行了。那麽現在有了年齡限制顯然一棵線段樹是不行的,因此要把年齡離散化後從小到大每種年齡建一棵可持久化線段樹,求年齡區間只要把對應可持久化線段樹相減剩下的就只是對應年齡區間的點到根的標記。註意要開longlong。

#include<set>
#include<map>
#include<queue>
#include<stack>
#include<cmath>
#include<vector>
#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>
#define ll long long
using namespace std;
ll ans;
int n,m;
int tot;
int cnt;
int num;
int A,k;
int l,r;
int x,y,z;
int p[150010];
int h[150010];
int f[150010];
int g[150010];
int q[150010];
int v[150010];
int to[300010];
ll s[20000010];
ll sum[150010];
int dep[150010];
int t[20000010];
int son[150010];
int top[150010];
int val[300010];
int head[150010];
int size[150010];
int next[300010];
int root[150010];
int ls[20000010];
int rs[20000010];
ll total[150010];
struct node
{
    int x;
    int id;
}a[150010];
bool cmp(node a,node b)
{
    return a.x<b.x;
}
void add(int x,int y,int z)
{
    tot++;
    next[tot]=head[x];
    head[x]=tot;
    to[tot]=y;
    val[tot]=z;
}
void dfs(int x)
{
    size[x]=1;
    for(int i=head[x];i;i=next[i])
    {
        if(to[i]!=f[x])
        {
            dep[to[i]]=dep[x]+val[i];
            f[to[i]]=x;
            v[to[i]]=val[i];
            dfs(to[i]);
            size[x]+=size[to[i]];
            if(size[to[i]]>size[son[x]])
            {
                son[x]=to[i];
            }
        }
    }
}
void dfs2(int x,int tp)
{
    top[x]=tp;
    p[x]=++num;
    q[num]=x;
    if(son[x])
    {
        dfs2(son[x],tp);
    }
    for(int i=head[x];i;i=next[i])
    {
        if(to[i]!=f[x]&&to[i]!=son[x])
        {
            dfs2(to[i],to[i]);
        }
    }
}
void pushup(int rt,int l,int r)
{
    s[rt]=s[ls[rt]]+s[rs[rt]]+t[rt]*(sum[r]-sum[l-1]);
}
void change(int &rt,int pre,int l,int r,int L,int R)
{
    rt=++cnt;
    ls[rt]=ls[pre];
    rs[rt]=rs[pre];
    s[rt]=s[pre];
    t[rt]=t[pre];
    if(L<=l&&r<=R)
    {
        t[rt]++;
        s[rt]+=sum[r]-sum[l-1];
        return ;
    }
    int mid=(l+r)>>1;
    if(L<=mid)
    {
        change(ls[rt],ls[pre],l,mid,L,R);
    }
    if(R>mid)
    {
        change(rs[rt],rs[pre],mid+1,r,L,R);
    }
    pushup(rt,l,r);
}
ll query(int rt,int l,int r,int L,int R)
{
    if(L<=l&&r<=R)
    {
        return s[rt];
    }
    ll res=1ll*t[rt]*(sum[min(r,R)]-sum[max(l,L)-1]);
    int mid=(l+r)>>1;
    if(L<=mid)
    {
        res+=query(ls[rt],l,mid,L,R);
    }
    if(R>mid)
    {
        res+=query(rs[rt],mid+1,r,L,R);
    }
    return res;
}
void updata(int x,int rt)
{
    while(top[x]!=1)
    {
        change(root[rt],root[rt],1,n,p[top[x]],p[x]);
        x=f[top[x]];
    }
    change(root[rt],root[rt],1,n,1,p[x]);
}
ll find(int x,int l,int r)
{
    ll res=0;
    while(top[x]!=1)
    {
        res+=query(root[r],1,n,p[top[x]],p[x])-query(root[l],1,n,p[top[x]],p[x]);
        x=f[top[x]];
    }
    res+=query(root[r],1,n,1,p[x])-query(root[l],1,n,1,p[x]);
    return res;
}
int main()
{
    scanf("%d%d%d",&n,&m,&A);
    for(int i=1;i<=n;i++)
    {
        scanf("%d",&a[i].x);
        h[i]=a[i].x;
        a[i].id=i;
    }
    sort(h+1,h+n+1);
    k=unique(h+1,h+1+n)-h-1;
    for(int i=1;i<n;i++)
    {
        scanf("%d%d%d",&x,&y,&z);
        add(x,y,z);
        add(y,x,z);
    }
    dfs(1);
    dfs2(1,1);
    sort(a+1,a+1+n,cmp);
    for(int i=1;i<=n;i++)
    {
        sum[i]=sum[i-1]+v[q[i]];
    }
    for(int i=1;i<=n;i++)
    {
        int fx=lower_bound(h+1,h+1+k,a[i].x)-h;
        g[fx]=i;
        if(fx==(lower_bound(h+1,h+1+k,a[i-1].x)-h))
        {
            total[fx]=total[fx]+1ll*dep[a[i].id];
        }
        else
        {
            total[fx]=total[fx-1]+1ll*dep[a[i].id];
            root[fx]=root[fx-1];
        }
        updata(a[i].id,fx);
    }
    for(int i=1;i<=m;i++)
    {
        scanf("%d%d%d",&z,&x,&y);
        l=min((x+ans)%A,(y+ans)%A);
        r=max((x+ans)%A,(y+ans)%A);
        l=upper_bound(h+1,h+1+k,l-1)-h-1;
        r=upper_bound(h+1,h+1+k,r)-h-1;
        ans=total[r]-total[l]+1ll*(g[r]-g[l])*dep[z]-2*find(z,l,r);
        printf("%lld\n",ans);
    }
}

BZOJ4012[HNOI2015]開店——樹鏈剖分+可持久化線段樹