BZOJ4765 普通計算姬

2022-05-09 16:59:50 字數 4503 閱讀 2956

[bzoj4765]普通計算姬

試題描述

"奮戰三星期,造台計算機"。小g響應號召,花了三小時造了臺普通計算姬。普通計算姬比普通計算機要厲害一些。普通計算機能計算數列區間和,而普通計算姬能計算樹中子樹和。更具體地,小g的計算姬可以解決這麼個問題:給定一棵n個節點的帶權樹,節點編號為1到n,以root為根,設sum[p]表示以點p為根的這棵子樹中所有節點的權值和。計算姬支援下列兩種操作:

1 給定兩個整數u,v,修改點u的權值為v。

2 給定兩個整數l,r,計算sum[l]+sum[l+1]+....+sum[r-1]+sum[r]

儘管計算姬可以很快完成這個問題,可是小g並不知道它的答案是否正確,你能幫助他嗎?

輸入

第一行兩個整數n,m,表示樹的節點數與操作次數。

接下來一行n個整數,第i個整數di表示點i的初始權值。

接下來n行每行兩個整數ai,bi,表示一條樹上的邊,若ai=0則說明bi是根。

接下來m行每行三個整數,第乙個整數op表示操作型別。

若op=1則接下來兩個整數u,v表示將點u的權值修改為v。

若op=2則接下來兩個整數l,r表示詢問。

n<=10^5,m<=10^5

0<=di,v<2^31,1<=l<=r<=n,1<=u<=n

輸出

對每個操作型別2輸出一行乙個整數表示答案。

輸入示例

640

0340

1011

2232

4355

6212

1112

3623

5

輸出示例

16

109

資料規模及約定

見「輸入

題解

做法一:

我們先把 sum 陣列求出來,然後節點 u 權值的修改對應著 u 到根節點這一條鏈的修改,於是可以樹鏈剖分套資料結構完成這個操作;對於詢問,它問的卻是連續的一段編號;所以可以看成乙個二維平面,每個節點 u 的座標是 (dfs[u], u)(dfs[u] 表示節點 u 的樹鏈剖分序),權值就是 sum[u];那麼對於修改操作,就是 x 軸上連續的 log(n) 段,y 座標沒有限制;對於詢問操作就是 y 軸上連續一段, x 座標沒限制;所以這個東西就可以用 kd 樹維護了。

#include #include #include #include #include #include using namespace std;

const int buffersize = 1 << 16;

char buffer[buffersize], *head, *tail;

inline char getchar()

return *head++;

}int read()

while(isdigit(c))

return x * f;

}#define maxn 100010

#define maxm 200010

#define ul unsigned long long

#define ll long long

int n, m, head[maxn], nxt[maxm], to[maxm], val[maxn];

void addedge(int a, int b)

int rt, fa[maxn], siz[maxn], son[maxn], top[maxn], pos[maxn], tot;

ul sum[maxn];

void build(int u)

return ;

}void gett(int u, int tp)

int rt, ch[maxn][2];

bool cur;

struct node

node(int x, int y, ll val): val(val), add(0)

bool operator < (const node& t) const

} ns[maxn];

void maintain(int o)

ns[o].val += ns[o].add;

ns[o].sum += ns[o].add * ns[o].siz;

return ;

}void build(int& o, int l, int r, bool cur)

void pushdown(int o)

ns[o].add = 0;

return ;

}void upd(int o, int l, int r, int add)

if(l <= ns[o].x[0] && ns[o].x[0] <= r) ns[o].val += add;

for(int i = 0; i < 2; i++)

if(ch[o][i] && l <= ns[ch[o][i]].mx[0] && ns[ch[o][i]].mn[0] <= r)

upd(ch[o][i], l, r, add);

return maintain(o);

}ul que(int o, int l, int r)

void update(int u, int add)

#define maxol 2100000

char output[maxol];

int num[21], cnt, cntol;

int main()

build(rt);

gett(rt, rt);

for(int i = 1; i <= n; i++) ns[i] = node(pos[i], i, sum[i]);

build(rt, 1, n, 0);

while(q--)

else

} output[--cntol] = '\0';

puts(output);

return 0;

}

然而加了讀入輸出優化還是 t 飛。。。。。

解法二:

分塊套分塊。

我們先搞乙個 dfs 序列,序列上存 val[i](即節點 i 的權值)。然後我們對這個序列分塊,並維護兩個資訊:dfs[i] 表示位置 i 所在塊的字首和,dfsb[i] 表示前 i 個塊的總和(即塊的字首和)。這樣我們就可以 o(1) 詢問區間和,o(sqrt(n)) 點修改了(想一想,為什麼)。

然後我們再對正常編號的序列分塊,並維護兩個資訊:tot[i][j] 表示第 i 塊中所有 sum 使得對應 dfs 序列上位置 j 被計算了幾次,sb[i] 表示第 i 塊中 sum 的總和。那麼借助 tot 我們可以 o(n · sqrt(n)) 預處理 sb,還可以 o(sqrt(n)) 支援點修改(想一想,為什麼)。查詢 [l, r] 時,對於被整個覆蓋的塊 i 直接累加 sb[i] 就好了,對於沒有被整個覆蓋的塊我們暴力找到這些點對應的 dfs 序上的區間,然後 o(1) 詢問區間和,累加,就好了。

#include #include #include #include #include #include #include using namespace std;

int read()

while(isdigit(c))

return x * f;

}#define maxn 100010

#define maxm 200010

#define maxb 320

#define ul unsigned long long

#define ll long long

int n, m, head[maxn], nxt[maxm], to[maxm];

void addedge(int a, int b)

int rt, clo, dl[maxn], dr[maxn], id[maxn], val[maxn];

void build(int u, int fa)

ul dfs[maxn], dfsb[maxb], sb[maxb];

int tot[maxb][maxn], bl[maxn], st[maxn], en[maxn];

ul que(int l, int r)

int main()

build(rt, 0);

int m = (int)sqrt(n);

for(int i = 1; i <= n; i++)

// for(int i = 1; i <= bl[n]; i++) printf("[%d, %d]\n", st[i], en[i]);

for(int i = 1; i <= bl[n]; i++)

while(q--)

else

printf("%llu\n", ans);

} }return 0;

}

bzoj 4765 普通計算姬

求樹上一段連續編號的子樹大小的和,帶修改。分塊。預處理乙個東西g x i 表示x這個點對i這個塊的影響。然後就可以暴力搞了。對於零散的點,就用樹狀陣列維護dfs序的區間和。貌似是nn logn 的,不過就是能過。還有以後打 盡量注意,少遞迴,少遞迴,少遞迴!最後要用un sign edlo nglo...

BZOJ 4765 普通計算姬

bzoj 4765 普通計算姬 分塊 奮戰三星期,造台計算機 小g響應號召,花了三小時造了臺普通計算姬。普通計算姬比普通計算機要厲害一些 普通計算機能計算數列區間和,而普通計算姬能計算樹中子樹和。更具體地,小g的計算姬可以解決這麼個問題 給定一棵n個節點的帶權樹,節點編號為1到n,以root為根,設...

bzoj4765 普通計算姬

給定一棵 n 個節點的帶權樹,節點編號為 1 n 以 root 為根,設 sum p 表示以點 p 為根的這棵子樹中所有節點的權值和。支援下列兩種操作 1.給定兩個整數 u,v 修改點 u 的權值為 v 2.給定兩個整數 l,r 計算 sum sum 第一行兩個整數 n,m 表示樹的節點數與操作次數...