bzoj 3589 動態樹 (樹鏈剖分 線段樹)

2021-07-23 02:31:11 字數 2540 閱讀 8085

time limit: 30 sec  

memory limit: 1024 mb

submit: 451  

solved: 155 [

submit][

status][

discuss]

別忘了這是一棵動態樹, 每時每刻都是動態的. 小明要求你在這棵樹上維護兩種事件

事件0:

這棵樹長出了一些果子, 即某個子樹中的每個節點都會長出k個果子.

事件1:

小明希望你求出幾條樹枝上的果子數. 一條樹枝其實就是乙個從某個節點到根的路徑的一段. 每次小明會選定一些樹枝, 讓你求出在這些樹枝上的節點的果子數的和. 注意, 樹枝之間可能會重合, 這時重合的部分的節點的果子只要算一次.

第一行乙個整數n(1<=n<=200,000), 即節點數.

接下來n-1行, 每行兩個數字u, v. 表示果子u和果子v之間有一條直接的邊. 節點從1開始編號.

在接下來乙個整數nq(1<=nq<=200,000), 表示事件.

最後nq行, 每行開頭要麼是0, 要麼是1.

如果是0, 表示這個事件是事件0. 這行接下來的2個整數u, delta表示以u為根的子樹中的每個節點長出了delta個果子.

如果是1, 表示這個事件是事件1. 這行接下來乙個整數k(1<=k<=5), 表示這次詢問涉及k個樹枝. 接下來k對整數u_k, v_k, 每個樹枝從節點u_k到節點v_k. 由於果子數可能非常多, 請輸出這個數模2^31的結果.

對於每個事件1, 輸出詢問的果子數. 5

1 22 3

2 41 5

30 1 1

0 2 3

1 2 3 1 1 4

131 <= n <= 200,000, 1 <= nq <= 200,000, k = 5.

生成每個樹枝的過程是這樣的:先在樹中隨機找乙個節點, 然後在這個節點到根的路徑上隨機選乙個節點, 這兩個節點就作為樹枝的兩端.

by 佚名提供 [

submit][

status][

discuss]

題解:樹鏈剖分+線段樹

這道題的操作1直接找到dfs序中該子樹所對應的區間,進行線段樹的區間加操作即可。

操作2,麻煩之處在於他給出的樹枝存在重疊的情況,而我們不進行重複的計算。於是我們給線段樹增加乙個標記。就是區間覆蓋標記,同時記錄一下區間中被覆蓋的總值,針對每個樹枝用樹鏈剖分求解答案(區間總值-區間被覆蓋的值)即可,然後將該樹枝對應的區間打標記,更新覆蓋總值。樹枝全部處理完成後,記得把覆蓋標記清零。

這麼做常數有點大,不過非常好寫也比較好想。。。。

#include#include#include#include#include#define n 400003

#define ll long long

using namespace std;

int m,n;

int point[n],next[n],tot,v[n],sz,a[n],b[n],cnt,l[n],r[n];

int size[n],deep[n],son[n],belong[n],f[n],pos[n];

ll tr[n*4],delta[n*4],rev[n*4],tr1[n*4],p;

void add(int x,int y)

void build(int x,int fa)

r[x]=cnt;

}void dfs(int k,int chain)

void update(int now)

void pushdown(int now,int l,int r)

if (rev[now]) }

void qjchange(int now,int l,int r,int ll,int rr,ll z)

int mid=(l+r)/2;

pushdown(now,l,r);

if (ll<=mid) qjchange(now<<1,l,mid,ll,rr,z);

if (rr>mid) qjchange(now<<1|1,mid+1,r,ll,rr,z);

update(now);

}void qjrev(int now,int l,int r,int ll,int rr,ll z)

int mid=(l+r)/2;

pushdown(now,l,r);

if (ll<=mid) qjrev(now<<1,l,mid,ll,rr,z);

if (rr>mid) qjrev(now<<1|1,mid+1,r,ll,rr,z);

update(now);

}ll qjsum(int now,int l,int r,int ll,int rr)

ll solve(int x,int y)

void solve2(int x,int y)

int main()

{ freopen("a.in","r",stdin);

freopen("my.out","w",stdout);

scanf("%d",&n); p=2147483648ll;

for (int i=1;i

樹鏈剖分 BZOJ3589 動態樹

time limit 30 sec memory limit 1024 mb submit 543 solved 193 submit status discuss 別忘了這是一棵動態樹,每時每刻都是動態的.小明要求你在這棵樹上維護兩種事件 事件0 這棵樹長出了一些果子,即某個子樹中的每個節點都會長...

BZOJ 3589 動態樹 樹鏈剖分 線段樹

題目傳送門 最近心真的有點浮躁啊 連題目都不想好好看了 於是就把 一條樹枝其實就是乙個從某個節點到根的路徑的一段 看成了 一條樹枝其實就是乙個從某個節點到根的路徑 wqnmlgb 操作1的k 5 那麼是不是會想到容斥?對兩條路徑求交?但是分析一下時間複雜度,o m log22n 2k 接近20億啊 ...

BZOJ3589 動態樹 樹鏈剖分 線段樹

別忘了這是一棵動態樹,每時每刻都是動態的.小明要求你在這棵樹上維護兩種事件 事件0 這棵樹長出了一些果子,即某個子樹中的每個節點都會長出k個果子.事件1 小明希望你求出幾條樹枝上的果子數.一條樹枝其實就是乙個從某個節點到根的路徑的一段.每次小明會選定一些樹枝,讓你求出在這些樹枝上的節點的果子數的和....