1. 程式人生 > >ACM-ICPC 2018 瀋陽賽區網路預賽 J. Ka Chang (分塊+dfs序+樹狀陣列)

ACM-ICPC 2018 瀋陽賽區網路預賽 J. Ka Chang (分塊+dfs序+樹狀陣列)

題目連結

題意:

給你一顆n個節點的樹,每一個節點的值為0。

q種操作,1.將深度為L的點的權值加x,根節點的深度為0

2.輸出以x為根節點的子樹的權值和

解析:

這道題最後想到了分塊,因為我想到用的一種方法需要用[1e5][1e5]的空間去維護,有空間問題,

然後藉此想到分塊可以處理,小的部分暴力,大的部分用陣列儲存狀態,這樣就存的下了

不過我想的是按層來分塊,前block層,和後block層,最後發現並沒有什麼用

搜了題解,是按照每一層內的點的個數來分類的,因為最暴力的做法,就是每一次修改,

把該層每一個點都更新一遍,所以我們就將每一層按照頂點數分塊,

對於點>block的層稱為重層,點<block的層稱為輕層。

題解的這個分塊方法主要不是解決空間的問題,而是通過block來解決時間的問題

對於輕層,因為點的個數<block,所以我們抓住這個,更新的時候用暴力更新,每一個點

對應樹狀陣列更新一遍,時間O(block*logn),然後查詢的時候,就用樹狀陣列查詢O(logn)

對於重層,重層的層的數量是n/block,我們抓住這個,更新的時候,就將更新的值儲存在數組裡O(1),

查詢的時候再遍歷每一層查詢O(n/block*logn)

最後就是一些細節了,對於樹狀陣列插入的時候,我們是按照每一個點的dfs序下標插入的,這樣

我們查詢x的子樹時,我們只要知道x的dfs序和x的子樹大小,就可以在樹狀陣列中查到該子樹內輕層

的點的權值和

然後對於重層,更新的時候只要存每一個重層更新的權值和,然後查詢x的子樹的時候,從x層遍歷

在它下面的重層,對於每一個重層,依照dfs序進行兩次二分,查出該層屬於x的子數的點的個數inc

再*mark,就是答案了

#include <cstdio>
#include <cstring>
#include <cmath>
#include <algorithm>
#include <set>
#include <vector>
#include <map>
using namespace std;
typedef long long ll;
const int MAXN = 1e5+10;

vector<int> edge[MAXN];
vector<int> dep[MAXN];
map<int,int> mdfs;
int sz[MAXN];
int md[MAXN];
ll c[MAXN];
ll mark[MAXN];
int n;
int maxdep,big[MAXN],cnt;

int lowbit(int x)
{
	return x&(-x);
}

void add(int x,int y)
{
	int i;
	for(i=x;i<=n;i+=lowbit(i))
		c[i]+=y;
}

ll sum(int x)
{
	int i;
	ll s=0;
	for(i=x;i>0;i-=lowbit(i))
	{
		s+=c[i];
	}
	return s;
}

int dfs(int x,int d,int &ind)
{
    maxdep=max(maxdep,d);
    mdfs[x]=ind;
    dep[d].push_back(ind);
    md[x]=d;
    sz[x]=1;
    for(int i=0;i<edge[x].size();i++)
    {
        ind++;
        sz[x]+=dfs(edge[x][i],d+1,ind);
    }
    return sz[x];
}



int main()
{
    int q;

    scanf("%d%d",&n,&q);
    int block=sqrt(n);
    int x,y;
    maxdep=0;
    for(int i=1;i<n;i++)
    {
        scanf("%d%d",&x,&y);
        edge[x].push_back(y);
    }

    int ind=1;
    dfs(1,1,ind);
    cnt=0;
    for(int i=1;i<=maxdep;i++)
    {
        if(dep[i].size()>block) big[cnt++]=i;
    }

    int id;
    for(int i=0;i<q;i++)
    {
        scanf("%d",&y);
        if(y==1)
        {
            scanf("%d%d",&x,&id);
			x++;
            if(dep[x].size()<=block)
            {
                for(int j=0;j<dep[x].size();j++)
                    add(dep[x][j],id);
            }
            else
            {
                mark[x]+=id;
            }
        }
        else
        {
            scanf("%d",&x);
            int nex=mdfs[x]+sz[x];
            ll res=sum(nex-1)-sum(mdfs[x]-1);
            int beg=lower_bound(big,big+cnt,md[x])-big;
            for(int j=beg;j<cnt;j++)
            {
				int h=big[j];
                int inc=upper_bound(dep[h].begin(),dep[h].end(),nex-1)-(lower_bound(dep[h].begin(),dep[h].end(),mdfs[x]));
                res+=(inc)*mark[h];
            }
            printf("%lld\n",res);
        }
    }
    return 0;
}