[ACNOI2022] the only thing to do

Posted by oocuz on Fri, 25 Feb 2022 12:58:07 +0100


Topic background
Write for two consecutive days LCT + ddp \texttt{LCT}+\texttt{ddp} LCT+ddp, I still feel it when I look at it today d d p \tt ddp ddp, plus I've spent all my time T 1 , T 2 T_1,T_2 T1, T2, finally abandoned.

Unfortunately, it killed a comeback, this time d d p \tt ddp The form of ddp is very special. Unfortunately, I can't jump out of my thinking mode, so I can't surpass it D D ( X Y X ) \sf DD(XYX) DD(XYX) . Unfortunately, O U Y E \sf OUYE OUYE has never made such a mistake. Take it easy A C AC AC .

Title Description
give n n n-point tree, point has point weight v i v_i vi. conduct q q q operations, there are three types: single point plus v i v_i vi, subtree plus v i v_i vi. query ∑ 1 ⩽ i < j ⩽ n [ lca ( i , j ) = x ] ⋅ v i v j \sum_{1\leqslant i<j\leqslant n}[\text{lca}(i,j)=x]\cdot v_iv_j Σ 1 ⩽ I < J ⩽ n [lca(i,j)=x] ⋅ vi ⋅ vj, the output result is right 2 63 2^{63} 263 take mold.

Data scope and agreement
max ⁡ ( n , q ) ⩽ 2 × 1 0 5 \max(n,q)\leqslant 2\times 10^5 max(n,q)⩽2 × 105, but we should be able to do better. Modification amount Δ v ∈ [ 0 , 1 0 9 ] \Delta v\in[0,10^9] Δ v ∈ [0109], initial value v i ∈ [ 0 , 1 0 9 ] v_i\in[0,10^9] vi ∈ [0109], but this is not important.


I thought it was a relatively large matrix d d p \tt ddp ddp . Now it seems that too much useless work has been done. remember S x S_x Sx is x x In x subtree v i v_i vi) sum of, then
a n s ( x ) = 1 2 ( S x   2 − v x   2 − ∑ y ∈ son x S y   2 ) ans(x)=\frac{1}{2}\left(S_x^{\thinspace 2}-v_x^{\thinspace 2}-\sum_{y\in\text{son}_x} S_y^{\thinspace 2}\right) ans(x)=21​(Sx2​−vx2​−y∈sonx​∑​Sy2​)

It is worth noting that a n s ( x ) ans(x) ans(x) is not used for transfer, but to find the answer, so it does not need to be put into the matrix. By the same token S x   2 S_x^{\thinspace 2} Sx2 doesn't need to be put into the matrix. To put it bluntly, only S x S_x Sx# uploading.

After the heavy chain is divided, the light chain is directly maintained for each point S y   2 S_y^{\thinspace 2} Sum of Sy2 +; own S x S_x Sx# also needs maintenance. For the answer, use the known ∑ S y   2 \sum S_y^{\thinspace 2} Σ Sy2 and heavy son S w S_w Sw and his S x , v x S_x,v_x Sx, vx} direct calculation. use unsigned   long   long \texttt{unsigned long long} unsigned , long , long can calculate the modulus 2 64 2^{64} 264, divided by 2 2 2 is just right.

Subtree modification for d d p \tt ddp ddp is not friendly. When it is difficult to update the information of all points immediately, try lazy marking. Leave the "subtree plus" on the operated node (the root of the subtree). For ancestors above this point, it is difficult to "see" the mark of this point, so it is best to update it in advance; For the points in the subtree, you can consider it again when querying - find out which lazy tags are "covered" by the current subtree, that is, these tags act on the whole subtree. Then the actual need to find ∑ y ∈ son x ( S y + size y ⋅ d ) 2 = ∑ y S y   2 + 2 d ∑ y S y ⋅ size y + d 2 ∑ y size y   2 \sum_{y\in\text{son}_x}(S_y+\text{size}_y\cdot d)^2=\sum_{y}S_y^{\thinspace 2}+2d\sum_{y}S_y\cdot\text{size}_y+d^2\sum_y\text{size}_y^{\thinspace 2} ∑y∈sonx​​(Sy​+sizey​⋅d)2=∑y​Sy2​+2d∑y​Sy​⋅sizey​+d2∑y​sizey2​ .

So, can you work out ∑ S y ⋅ size y \sum S_y\cdot \text{size}_y Σ Sy ⋅ sizey, after all ∑ size y   2 \sum\text{size}_y^{\thinspace 2} ∑ sizey2 ∑ is the fixed value. Just save it. Of course, the value of lazy tags outside the subtree is not considered.

There is no need for matrix and segment tree to maintain heavy chain, because S x S_x Sx ^ has practical significance and can be used d f n \tt dfn Segment tree on dfn O ( log ⁡ n ) \mathcal O(\log n) O(logn) direct query. Code implementation is also relatively easy, time complexity O ( n log ⁡ n + q log ⁡ n ) \mathcal O(n\log n+q\log n) O(nlogn+qlogn), where n log ⁡ n n\log n nlogn can also be optimized as O ( n ) \mathcal O(n) O(n), but not necessary.


#include <cstdio> // JZM yydJUNK!!!
#include <iostream> // XJX yyds!!!
#include <algorithm> // XYX yydLONELY!!!
#include <cstring> // (the STRONG long for LONELINESS)
#include <cctype> // ZXY yydSISTER!!!
using namespace std;
# define rep(i,a,b) for(int i=(a); i<=(b); ++i)
# define drep(i,a,b) for(int i=(a); i>=(b); --i)
typedef long long llong;
inline int readint(){
	int a = 0, c = getchar(), f = 1;
	for(; !isdigit(c); c=getchar())
		if(c == '-') f = -f;
	for(; isdigit(c); c=getchar())
		a = (a<<3)+(a<<1)+(c^48);
	return a*f;
inline void writeint(const uint64_t &x){
	if(x > 9) writeint(x/10);

const int MAXN = 200005;
struct Edge{ int to, nxt; };
Edge e[MAXN<<1]; int head[MAXN], cntEdge;
inline void addEdge(int a, int b){
	e[cntEdge].to = b, e[cntEdge].nxt = head[a];
	head[a] = cntEdge ++;
# define _go(i,x) for(int i=head[x]; ~i; i=e[i].nxt)

unsigned int siz[MAXN];
int heavy[MAXN], fa[MAXN];
void scan(int x){
	siz[x] = 1; _go(i,x){
		fa[e[i].to] = x, scan(e[i].to);
		siz[x] += siz[e[i].to];
		if(siz[e[i].to] > siz[heavy[x]])
			heavy[x] = e[i].to; // node indice

unsigned int lsiz[MAXN], v[MAXN];
int dfn[MAXN], ed[MAXN], top[MAXN], dfsClock;
uint64_t lig[MAXN][2], me[MAXN];
void dfs(int x, int bel){
	dfn[x] = ++ dfsClock, top[x] = bel;
	if(heavy[x]) dfs(heavy[x],bel), me[x] = me[heavy[x]];
	_go(i,x) if(e[i].to != heavy[x]){
		dfs(e[i].to,e[i].to), me[x] += me[e[i].to];
		lsiz[x] += siz[e[i].to]*siz[e[i].to];
		lig[x][0] += me[e[i].to]*siz[e[i].to];
		lig[x][1] += me[e[i].to]*me[e[i].to];
	me[x] += v[x], ed[x] = dfsClock;

/// @brief add value of @p x by @p qv
void update_chain(int x, const uint64_t &qv){
	for(x=top[x]; x!=1; x=top[fa[x]]){
		lig[fa[x]][1] += (qv+(me[x]<<1))*qv;
		lig[fa[x]][0] += qv*siz[x], me[x] += qv;

/// @brief add on range and query a single point (for calculate tag)
namespace Tagger{
	uint64_t c[MAXN];
	void modify(int ql, int qr, const unsigned &qv){
		for(int i=ql; i<=dfsClock; i+=(i&-i)) c[i] += qv;
		for(int i=qr+1; i<=dfsClock; i+=(i&-i)) c[i] -= qv;
	uint64_t query(int qid){
		uint64_t res = 0;
		for(int i=qid; i; i&=(i-1)) res += c[i];
		return res;
/// @brief add on range and query a range sum (for calculate S_x)
namespace SgTree{
	uint64_t one[MAXN], two[MAXN];
	void _modify(int qid, const uint64_t &qv){
		const uint64_t tmp = qv*unsigned(qid);
		for(int i=qid; i<=dfsClock; i+=(i&-i))
			one[i] += qv, two[i] += tmp;
	uint64_t _query(int qid){
		uint64_t res = 0, sum = 0;
		for(int i=qid; i; i&=(i-1))
			res += two[i], sum += one[i];
		return sum*unsigned(qid+1u)-res;
	void modify(int ql, int qr, const unsigned &qv){
		_modify(ql,qv), _modify(qr+1,-uint64_t(qv));
	inline uint64_t query(int ql, int qr){
		return _query(qr)-_query(ql-1);

uint64_t getAns(int x){
	if(!heavy[x]) return 0u; // leaf
	const uint64_t d = Tagger::query(dfn[x]);
	uint64_t my = SgTree::query(dfn[x],ed[x]); // real
	uint64_t him = SgTree::query(dfn[heavy[x]],ed[heavy[x]]);
	uint64_t alone = SgTree::query(dfn[x],dfn[x]);
	him = him*him+lig[x][1]+((lig[x][0]<<1)+lsiz[x]*d)*d;
	return my*my-alone*alone-him;

char opt[10];
int main(){
	int n = readint(), q = readint();
	memset(head+1,-1,n<<2), cntEdge = 0;
	for(int i=1; i!=n; ++i) addEdge(readint(),i+1);
	rep(i,1,n) v[i] = readint();
	scan(1), dfs(1,1); // basic info
	rep(i,1,n) SgTree::modify(dfn[i],dfn[i],v[i]);
	for(unsigned x,y; q; --q){
		scanf("%s",opt), x = readint();
		if(*opt == 'Q'){
		else if(*opt == 'S'){
			update_chain(x, y = readint());
		else if(*opt == 'M'){
			y = readint();
	return 0;

Topics: C++ Dynamic Programming