1. 程式人生 > >Gym 100589A Queries on the Tree (樹狀陣列+分塊均攤思想)

Gym 100589A Queries on the Tree (樹狀陣列+分塊均攤思想)

#include<bits/stdc++.h>
using namespace std;

#define debug puts("YES");
#define rep(x,y,z) for(int (x)=(y);(x)<(z);(x)++)
#define read(x,y) scanf("%d%d",&x,&y)
#define ll long long

#define lrt int l,int r,int rt
#define lson l,mid,rt<<1
#define rson mid+1,r,rt<<1|1
/*
題目大意:一棵樹兩種操作,
查詢子樹的權重和,修改同一層節點的權重。

同一層的節點的話就用vector來分層,記住要記錄
DFS區間序的左端點即可。

首先思考下,同一層的修改操作,
有兩種,一種是在層權重陣列中直接修改,O(1),
然後查詢時遍歷所有層,通過二分來統計節點累加權重。O(nlogn)
一種是樹狀陣列通過DFS序左端點修改,O(nlogn)
查詢時可以直接樹狀陣列查詢區間和O(logn)。
如果我們採用其中的任何一種,為什麼會超時呢?
因為存在某一層節點數量太多的情況,直接使複雜度弱化為O(nmlogn)。
這樣就要引入資料結構中常用的分塊的思想(不是數論中的分塊。。。)
個人感覺其實就是均攤,當深度節點數量大於X時,採用第二種修改,
否則採用第一種,至於查詢,N/x*logn級別的。因為這樣需要統計權重的層數
不超過N/x。
那麼我們把函式式子列出來發現x取根號N答案最優,根號N的界限就是這樣來的。

有了思路下面就是秀操作的時候了。
另外一個小優化是要事先把大於界限的層下標抽取出來。
*/
const int  maxn =1e5+5;
///資料範圍區域
int n,m,ub;
int op,x,y;
///深度節點集合和權重陣列
vector<int> g[maxn],big;
ll w[maxn];///資料範圍要注意
int maxd;
///鏈式前向星
struct node
{
    int u,nxt;
}edge[maxn<<1];
int head[maxn],tot=0,ti;
void init()
{
    big.clear();
    for(int i=0;i<=n;i++) g[i].clear();
    memset(w,0,sizeof(w));
    memset(head,-1,sizeof(head));
    tot=ti=maxd=0;
}
void add(int x,int y)
{
    edge[tot]=node{y,head[x]};
    head[x]=tot++;
}
///DFS序區間建立
int pl[maxn],pr[maxn];
void dfs(int u,int pre,int h)
{
    maxd=max(maxd,h);
    pl[u]=++ti;
    g[h].push_back(ti);
    for(int i=head[u];~i;i=edge[i].nxt)
    {
        int v=edge[i].u;
        if(v==pre) continue;
        dfs(v,u,h+1);
    }
    pr[u]=ti;
}
///bit
ll tree[maxn<<1];
int lowbit(int x)
{
    return x&(-x);
}
void refresh(int x,int d)
{
    for(;x<maxn;tree[x]+=d,x+=lowbit(x));
}
ll sum(int x)
{
    ll ret=0;
    for(;x>0;ret+=1LL*tree[x],x-=lowbit(x));
    return ret;
}
int main()
{
    init();
    scanf("%d%d",&n,&m);
    ub=sqrt(n);
    for(int i=1;i<n;i++)
    {
        scanf("%d%d",&x,&y);
        add(x,y),add(y,x);
    }
    dfs(1,-1,0);///建立DFS序
    for(int i=0;i<=maxd;i++)
    {
        if(g[i].size()<=ub) continue;
        sort(g[i].begin(),g[i].end());
        big.push_back(i);
    }
    for(int i=0;i<m;i++)
    {
        scanf("%d",&op);
        if(op==1)
        {
            scanf("%d%d",&x,&y);
            int sz=g[x].size();
            if(sz<=ub)///樹狀陣列暴力修改
            {
                for(int j=0;j<sz;j++)
                    refresh(g[x][j],y);
            }
            else
            {
                w[x]+=y;
            }
        }
        else
        {
            scanf("%d",&x);
            int l=pl[x],r=pr[x],sz=big.size();
            ll ans=1LL*(sum(r)-sum(l-1));///log查詢
            for(int j=0;j<sz;j++)
            {
                int d=big[j];
                int tx=upper_bound(g[d].begin(),g[d].end(),pr[x])-g[d].begin();
                int ty=lower_bound(g[d].begin(),g[d].end(),pl[x])-g[d].begin();
                ans+=1LL*(tx-ty)*w[d];
            }
            printf("%lld\n",ans);
        }
    }

    return 0;
}