[Brush questions] Luogu P3676 Small and fresh data structure questions

topic background

The time limit for this question is 2s, and the memory limit is 256M

Topic description

A long, long time ago, there was a tree of n points, each with a point weight.

Now there are q operations, each operation is to modify the point weight of a point or specify a point, and ask the sum of the squares of the point weights of each subtree when this point is the root.

(The topic is not very easy to understand, if you don't understand it too well, you can look at the sample explanation)

Input and output format

Input format:

The first line contains two integers n and q.

The next n-1 lines each have two integers a and b, indicating that there is an edge between a and b in the tree, ensuring that the given edge will not be repeated.

The next line contains n integers, and the ith integer represents the point weight of the ith point.

The next q lines have two or three numbers per line. If the first number is 1, then there are two numbers x and y next, which means to modify the point weight of the xth point to y. If the first number is 2, then there is a number x next, which means the sum of the squares of the weights of each subtree when x is the root.

Output format:

Output the answer for each query.

Input and output example

Input example #1:

4 5
1 2
2 3
2 4
4 3 2 1
2 2
1 1 3
2 3
1 2 4
2 4

Sample output #1:

121
140
194

illustrate

Example explanation

This is a chrysanthemum diagram with edges between 2 and 1, 3, and 4.

At the beginning, the weights of each point are 4, 3, 2, and 1 respectively.

The first query takes 2 as the root, there is only one point in the 1, 3, and 4 subtrees, and there are all points in the 2 subtree, then the weights of the points in the 1, 3, and 4 subtrees are their own points respectively. The sum of the point weights in the subtree of weights 4, 2, 1, and 2 is 4+3+2+1=10, \(4^2+2^2+1^1+10^2=121\) .

Next, modify the first point weight to 3, and each point weight is 3, 3, 2, and 1, respectively.

The second query takes 3 as the root. There is only oneself in the 1 and 4 subtrees, and there are 1, 2, and 4 in the 2 subtree. There are all points in the 3 subtree. 3, 1, 2 subtree point weight is 3+3+1=7, 3 subtree point weight is 3+3+2+1=9, \(3^2+1^2+7^2+ 9^2=140\) .

Next, modify the second point weight to 4, and each point weight is 3, 4, 2, and 1, respectively.

The third query takes 4 as the root, the point weights of 1 and 3 subtrees are 3 and 2, the weights of 2 subtrees are 3+4+2=9, and the weights of 4 subtrees are 3+4+2+ 1=10, \(3^2+2^2+9^2+10^2=194\) .

data range

For 10% of the data, \(n,q \leq 2000\) .

\(n,q \leq 60000\) for 40% of the data .

For the other 30% of the data, the root of each query is guaranteed to be 1.

For 100% data, \(1 \leq n,q \leq 200000\) , \(-10 \leq\) each point weight of the input \(\leq 10\) .

It is recommended to use input optimization, although the standard routine can be passed without read-in optimization

answer

There is no dynamic point divide and conquer, so the tree section is messed
up. The tree section adds a line segment tree. The line segment tree maintains an interval sum, a square of the interval sum, and a lazy mark. Considering partial division, what if
the root of all queries is 1? The
modified point is \(u\) , then the points affected by this modification are only the points on the path from \(u\) to \(1\) , only the subtree sum of these points and the square of the sum of the subtrees There is a change, consider this change
Let \(p=newval_u-oldval_u\) , that is, \(p\) is the weight of the \(u\) point to increase (or decrease) the
subtree of points on its path to the root and The modification is very simple, just add the line segment tree interval directly, look at the increase of the square of the sum, and map each chain after the cut to the interval, \(\sum (s_i+p)^2=\sum s_i^2+ 2s_ip+p^2=\sum s_i^2+2p\sum s_i+p^2len\) , you can know that the contribution of the sum of squares of all positions in this interval is the same, so it can also be lazy.
Then if there is a lazy mark \(p\) on an interval, first calculate the square of the new interval sum (because the formula is related to the old interval sum, if the interval sum is calculated first, the squared answer will be wrong), add directly \(2p\sum s_i+p^2len\) , then calculate the new interval sum, and
then the root of the query is 1, and the answer is the interval sum of the entire line segment tree.
Then see what to do if the root of the query is not 1.
Similarly, the root is set as 1 by default, and the line segment tree is maintained according to the method of root 1. You can get the answer of a tree with a root of 1 \(stans\)
and then see, How to change the answer whose root is 1 to the answer whose root is the point of
inquiry Suppose the point of inquiry is \(u\)
Similarly, after the shape of the tree is changed, the subtree and the change are only \(u\) to \( 1\) points on the path, sort these points according to the depth from small to large, and get the sequence \(p_1,p_2,p_3,...,p_{cnt}\)
Easy to get the formula, \(ans=stans- \sum_{i=1}^{cnt} a_{p_i}^2+\sum_{i=1}^{cnt}b_{p_i}^2\) , where \(a_i\) means when 1 is the root , the subtree sum of \(i\) , \(b_i\) means that when \(u\) is the root, the subtree sum of \(i\)
Then there is such an equation, \(b_{p_i}+ a_{p_{i+1}}=a_{p_1}=b_{p_{cnt}}\) , they are all equal to the square of the sum of the weights of the whole tree and
then the formula is simplified
\(ans=stans-\sum_{i=1}^{cnt} a_{p_i}^2+b_{p_{cnt}}^2+\sum_{i=1}^{cnt-1}(a_{ p_1}-a_{p_{i+1}})^2\)
\(~~~~~~~~~=stans+a_{p_1}^2-\sum_{i=1}^{cnt} a_{ p_i}^2+\sum_{i=1}^{cnt-1}a_{p_1}^2-2a_{p_1}a_{p_{i+1}}+a_{p_{i+1}}^2 \)
\(~~~~~~~~=stans+a_{p_1}^2-\sum_{i=1}^{cnt} a_{p_i}^2+(cnt-1)a_{p_1}^ 2-2a_{p_1}\sum_{i=2}^{cnt}a_{p_i}+\sum_{i=2}^{cnt}a_{p_i}^2\)
\(~~~~~~~ ~=stans+a_{p_1}^2-a_{p_1}^2+(cnt-1)a_{p_1}^2-2a_{p_1}\sum_{i=1}^{cnt}a_{p_i}+ 2a_{p_1}^2\)
\(~~~~~~~~~=stans+(cnt+1)a_{p_1}^2-2a_{p_1}\sum_{i=1}^{cnt}a_{p_i }\)
And \(cnt\) is actually the dep of \(u\) , \(\sum_{i=1}^{cnt}a_{p_i}\) is when 1 is the root \(u\) To the sum of the subtrees on the \(1\) path, this is already maintained in the line segment tree
Then just calculate the new answer directly, the shape of the tree maintained in the program does not need to be changed at all

#include<bits/stdc++.h>
#define ui unsigned int
#define ll long long
#define db double
#define ld long double
#define ull unsigned long long
const int MAXN=200000+10,inf=0x3f3f3f3f;
int n,q,e,to[MAXN<<1],nex[MAXN<<1],beg[MAXN],sum[MAXN],vl[MAXN],st[MAXN],ed[MAXN],cnt,fa[MAXN],top[MAXN],hson[MAXN],size[MAXN],val[MAXN],dep[MAXN];
ll stans;
#define Mid ((l+r)>>1)
#define ls rt<<1
#define rs rt<<1|1
#define lson ls,l,Mid
#define rson rs,Mid+1,r
struct Segment_Tree{
    ll Sum1[MAXN<<2],Sum2[MAXN<<2],Add[MAXN<<2];
    inline void PushUp(int rt)
    {
        Sum1[rt]=Sum1[ls]+Sum1[rs];
        Sum2[rt]=Sum2[ls]+Sum2[rs];
    }
    inline void PushDown(int rt,int len)
    {
        Sum2[ls]+=1ll*Add[rt]*Add[rt]*(len-(len>>1))+2ll*Add[rt]*Sum1[ls];
        Sum2[rs]+=1ll*Add[rt]*Add[rt]*(len>>1)+2ll*Add[rt]*Sum1[rs];
        Sum1[ls]+=1ll*Add[rt]*(len-(len>>1));
        Sum1[rs]+=1ll*Add[rt]*(len>>1);
        if(Add[ls]==inf)Add[ls]=0;
        if(Add[rs]==inf)Add[rs]=0;
        Add[ls]+=Add[rt];Add[rs]+=Add[rt];
        Add[rt]=inf;
    }
    inline void Build(int rt,int l,int r)
    {
        Add[rt]=inf;
        if(l==r)
        {
            Sum1[rt]=vl[l];
            Sum2[rt]=1ll*vl[l]*vl[l];
        }
        else
        {
            Build(lson);Build(rson);
            PushUp(rt);
        }
    }
    inline void Update(int rt,int l,int r,int L,int R,ll k)
    {
        if(L<=l&&r<=R)
        {
            Sum2[rt]+=1ll*k*k*(r-l+1)+2ll*k*Sum1[rt];
            Sum1[rt]+=1ll*(r-l+1)*k;
            if(Add[rt]==inf)Add[rt]=0;
            Add[rt]+=k;
        }
        else
        {
            if(Add[rt]!=inf)PushDown(rt,r-l+1);
            if(L<=Mid)Update(lson,L,R,k);
            if(R>Mid)Update(rson,L,R,k);
            PushUp(rt);
        }
    }
    inline ll Query(int rt,int l,int r,int L,int R)
    {
        if(L<=l&&r<=R)return Sum1[rt];
        else
        {
            ll res=0;
            if(Add[rt]!=inf)PushDown(rt,r-l+1);
            if(L<=Mid)res+=Query(lson,L,R);
            if(R>Mid)res+=Query(rson,L,R);
            return res;
        }
    }
};
Segment_Tree T;
#undef Mid
#undef ls
#undef rs
#undef lson
#undef rson
template<typename T> inline void read(T &x)
{
    T data=0,w=1;
    char ch=0;
    while(ch!='-'&&(ch<'0'||ch>'9'))ch=getchar();
    if(ch=='-')w=-1,ch=getchar();
    while(ch>='0'&&ch<='9')data=((T)data<<3)+((T)data<<1)+(ch^'0'),ch=getchar();
    x=data*w;
}
template<typename T> inline void write(T x,char ch='\0')
{
    if(x<0)putchar('-'),x=-x;
    if(x>9)write(x/10);
    putchar(x%10+'0');
    if(ch!='\0')putchar(ch);
}
template<typename T> inline void chkmin(T &x,T y){x=(y<x?y:x);}
template<typename T> inline void chkmax(T &x,T y){x=(y>x?y:x);}
template<typename T> inline T min(T x,T y){return x<y?x:y;}
template<typename T> inline T max(T x,T y){return x>y?x:y;}
inline void insert(int x,int y)
{
    to[++e]=y;
    nex[e]=beg[x];
    beg[x]=e;
}
inline void dfs(int x,int f)
{
    sum[x]=val[x];
    for(register int i=beg[x];i;i=nex[i])
        if(to[i]==f)continue;
        else dfs(to[i],x),sum[x]+=sum[to[i]];
}
inline void dfs1(int x,int f)
{
    int res=0;
    size[x]=1;fa[x]=f;dep[x]=dep[f]+1;
    for(register int i=beg[x];i;i=nex[i])
        if(to[i]==f)continue;
        else
        {
            dfs1(to[i],x);
            size[x]+=size[to[i]];
            if(size[to[i]]>res)res=size[to[i]],hson[x]=to[i];
        }
}
inline void dfs2(int x,int tp)
{
    st[x]=++cnt;vl[cnt]=sum[x];top[x]=tp;
    if(hson[x])dfs2(hson[x],tp);
    for(register int i=beg[x];i;i=nex[i])
        if(to[i]==fa[x]||to[i]==hson[x])continue;
        else dfs2(to[i],to[i]);
    ed[x]=cnt;
}
inline void init()
{
    dfs1(1,0);dfs2(1,1);
    T.Build(1,1,n);stans=T.Sum2[1];
}
inline void Doans(int x,ll k)
{
    while(x)
    {
        T.Update(1,1,n,st[top[x]],st[x],k);
        x=fa[top[x]];
    }
}
inline ll Getans(int x)
{
    if(x==1)return stans;
    ll a0=T.Query(1,1,n,st[1],st[1]),res=stans+1ll*(dep[x]+1)*a0*a0,nsum=0;
    while(x)
    {
        nsum+=T.Query(1,1,n,st[top[x]],st[x]);
        x=fa[top[x]];
    }
    return res-2ll*a0*nsum;
}
int main()
{
    read(n);read(q);
    for(register int i=1;i<n;++i)
    {
        int u,v;read(u);read(v);
        insert(u,v);insert(v,u);
    }
    for(register int i=1;i<=n;++i)read(val[i]);
    dfs(1,0);
    init();
    while(q--)
    {
        int opt;read(opt);
        if(opt==1)
        {
            int x,y;read(x);read(y);
            Doans(x,y-val[x]);
            val[x]=y;stans=T.Sum2[1];
        }
        if(opt==2)
        {
            int x;read(x);
            write(Getans(x),'\n');
        }
    }
    return 0;
}

Guess you like

Origin http://43.154.161.224:23101/article/api/json?id=324862011&siteId=291194637