1. 程式人生 > >BZOJ3307雨天的尾巴——線段樹合並

BZOJ3307雨天的尾巴——線段樹合並

class ret roo max can clas 修改 選擇 ext

題目描述

N個點,形成一個樹狀結構。有M次發放,每次選擇兩個點x,y
對於x到y的路徑上(含x,y)每個點發一袋Z類型的物品。完成
所有發放後,每個點存放最多的是哪種物品。

輸入

第一行數字N,M
接下來N-1行,每行兩個數字a,b,表示a與b間有一條邊
再接下來M行,每行三個數字x,y,z.如題

輸出

輸出有N行
每i行的數字表示第i個點存放最多的物品是哪一種,如果有
多種物品的數量一樣,輸出編號最小的。如果某個點沒有物品
則輸出0

樣例輸入

20 50
8 6
10 6
18 6
20 10
7 20
2 18
19 8
1 6
14 20
16 10
13 19
3 14
17 18
11 19
4 11
15 14
5 18
9 10
12 15
11 14 87
12 1 87
14 3 84
17 2 36
6 5 93
17 6 87
10 14 93
5 16 78
6 15 93
15 5 16
11 8 50
17 19 50
5 4 87
15 20 78
1 17 50
20 13 87
7 15 22
16 11 94
19 8 87
18 3 93
13 13 87
2 1 87
2 6 22
5 20 84
10 12 93
18 12 87
16 10 93
8 17 93
14 7 36
7 4 22
5 9 87
13 10 16
20 11 50
9 16 84
10 17 16
19 6 87
12 2 36
20 9 94
9 2 84
14 1 94
5 5 94
8 17 16
12 8 36
20 17 78
12 18 50
16 8 94
2 19 36
10 18 36
14 19 50
4 12 50

樣例輸出

87
36
84
22
87
87
22
50
84
87
50
36
87
93
36
94
16
87
50
50
1<=N,M<=100000
1<=a,b,x,y<=N
1<=z<=10^9
線段樹合並好題。 考慮對於一次操作可以看作是將x到根和y到根路徑上每個點加一個z物品,將x,y的lca和lca的父親到根路徑上每個點減一個z物品,也就是樹上差分。 因為要維護每個點物品數最多的物品是什麽,在每一個點動態開點建一棵權值線段樹維護這個點的每種物品數量。 每次操作樹上差分一下,在對應點的的權值線段樹上修改。 那麽一個點維護每種物品數的線段樹就是它子樹中每個點的線段樹合並後的樹。 因此從根節點dfs,回溯時將每個點的子樹權值線段樹合並到這個點的線段樹上並查詢物品數最多的物品就好了。 因為每次操作相當於在一棵線段樹上新建出一條鏈(實際上是在四棵樹上各建一條),那麽合並就相當於將4m條鏈合並,每兩條鏈合並是O(logn),總時間復雜度就是O(mlogn)。
#include<set>
#include<map>
#include<stack>
#include<queue>
#include<cmath>
#include<cstdio>
#include<vector>
#include<bitset>
#include<cstring>
#include<iostream>
#include<algorithm>
#define ll long long
using namespace std;
int h[100010];
int root[100010];
int ls[6000010];
int rs[6000010];
int next[200010];
int head[100010];
int to[200010];
int f[100010][18];
int d[100010];
int sum[6000010];
int ans[100010];
int tot;
int x,y;
int n,m;
int num;
int cnt;
struct miku
{
    int x;
    int y;
    int z;
}a[100010];
void add(int x,int y)
{
    tot++;
    next[tot]=head[x];
    head[x]=tot;
    to[tot]=y;
}
void dfs(int x)
{
    d[x]=d[f[x][0]]+1;
    for(int i=1;i<=17;i++)
    {
        f[x][i]=f[f[x][i-1]][i-1];
    }
    for(int i=head[x];i;i=next[i])
    {
        if(to[i]!=f[x][0])
        {
            f[to[i]][0]=x;
            dfs(to[i]);
        }
    }
}
int lca(int x,int y)
{
    if(d[x]<d[y])
    {
        swap(x,y);
    }
    int dep=d[x]-d[y];
    for(int i=0;i<=17;i++)
    {
        if((dep&(1<<i)))
        {
            x=f[x][i];
        }
    }
    if(x==y)
    {
        return x;
    }
    for(int i=17;i>=0;i--)
    {
        if(f[x][i]!=f[y][i])
        {
            x=f[x][i];
            y=f[y][i];
        }
    }
    return f[x][0];
}
void updata(int rt)
{
    if(ls[rt]&&rs[rt])
    {
        sum[rt]=max(sum[ls[rt]],sum[rs[rt]]);
    }
    else if(ls[rt])
    {
        sum[rt]=sum[ls[rt]];
    }
    else if(rs[rt])
    {
        sum[rt]=sum[rs[rt]];
    }
}
void insert(int &rt,int l,int r,int k,int v)
{
    if(!rt)
    {
        rt=++cnt;
    }
    if(l==r)
    {
        sum[rt]+=v;
        return ;
    }
    int mid=(l+r)>>1;
    if(k<=mid)
    {
        insert(ls[rt],l,mid,k,v);
    }
    else
    {
        insert(rs[rt],mid+1,r,k,v);
    }
    updata(rt);
}
void merge(int &rt,int x)
{
    if(!rt||!x)
    {
        rt=rt+x;
        return ;
    }
    sum[rt]+=sum[x];
    merge(ls[rt],ls[x]);
    merge(rs[rt],rs[x]);
    updata(rt);
}
int query(int rt,int l,int r)
{
    int mid=(l+r)>>1;
    if(l==r)
    {
        if(sum[rt]>0)
        {
            return l;
        }
        return 0;
    }
    if(sum[ls[rt]]>=sum[rs[rt]])
    {
        return query(ls[rt],l,mid);
    }
    else
    {
        return query(rs[rt],mid+1,r);
    }
}
void downdata(int x)
{
    for(int i=head[x];i;i=next[i])
    {
        if(to[i]!=f[x][0])
        {
            downdata(to[i]);
            merge(root[x],root[to[i]]);
        }
    }
    ans[x]=query(root[x],1,num);
}
int main()
{
    scanf("%d%d",&n,&m);
    for(int i=1;i<n;i++)
    {
        scanf("%d%d",&x,&y);
        add(x,y);
        add(y,x);
    }
    dfs(1);
    for(int i=1;i<=m;i++)
    {
        scanf("%d%d%d",&a[i].x,&a[i].y,&a[i].z);
        h[i]=a[i].z;
    }
    sort(h+1,h+1+m);
    num=unique(h+1,h+1+m)-h-1;
    for(int i=1;i<=m;i++)
    {
        int anc=lca(a[i].x,a[i].y);
        a[i].z=lower_bound(h+1,h+1+num,a[i].z)-h;
        insert(root[a[i].x],1,num,a[i].z,1);
        insert(root[a[i].y],1,num,a[i].z,1);
        insert(root[anc],1,num,a[i].z,-1);
        if(anc!=1)
        {
            insert(root[f[anc][0]],1,num,a[i].z,-1);
        }
    }
    downdata(1);
    for(int i=1;i<=n;i++)
    {
        printf("%d\n",h[ans[i]]);
    }
}

BZOJ3307雨天的尾巴——線段樹合並