[Luogu3676]小清新数据结构题

题面戳我
题意:给一棵树,树上有点权,每次操作为修改一个点的点权,或者是询问以某个点为根时,每棵子树(以每个点为根,就有n棵子树)点权和的平方和。
\(n\le2*10^5\),保证答案在long long范围内

sol

我们设\(s_i\)表示以\(p\)为整棵树的根时,以\(i\)为根的子树的点权和。设\(Sum\)表示所有点的点权和,即\(Sum=\sum_{i=1}^{n}val_i\)。
所以这道题给出\(p\),就是要你求\(\sum_{i=1}^{n}s_i^2\)。

我们先看\(\sum_{i=1}^{n}s_i\)怎么求。
考虑每个点的点权对\(\sum_{i=1}^{n}s_i\)的贡献,可以发现,每个点被计算了\(dep_i+1\)次,也就是说\(\sum_{i=1}^{n}s_i=\sum_{i=1}^{n}val_i(dep_i+1)=\sum_{i=1}^{n}val_idep_i+Sum\)。前面那一坨是不是有点熟悉?【ZJOI2015】幻想乡战略游戏
下文中为了方便描述,令\(calc(p)\)表示以\(p\)为根时的\(\sum_{i=1}^{n}val_idep_i\)

接下来我们考虑一下这个东西
\[\sum_{i=1}^{n}\sum_{j=1}^{n}val_ival_jdis(i,j)\]
这个可以形象地理解为,在每一对点对\((i,j)\)的路径上每一条边(刚好是\(dis(i,j)\)条边)上都加上\(val_ival_j\),然后求整棵树上的边权之和。

现在我们考虑每一条边上的权值,它应该等于它两侧连接的两坨树的点权和的乘积。而连接的这两坨树中,不论取哪个\(p\)为根,都有有且仅有一坨树会是一棵子树。所以这个权值会等于\(s_i(Sum-s_i)\)。所以
\[\sum_{i=1}^{n}\sum_{j=1}^{n}val_ival_jdis(i,j)=\sum_{i=1}^{n}s_i(Sum-s_i)\]
这同时也证明了不论取哪个\(p\)作为根,\(\sum_{i=1}^{n}s_i(Sum-s_i)\)都不会变。

令\(W=\sum_{i=1}^{n}s_i(Sum-s_i)\),可以先\(O(n)\)地\(DP\)出\(W\)的初值,然后就只要考虑一个点权修改对\(W\)的影响。
因为\(W=\sum_{i=1}^{n}\sum_{j=1}^{n}val_ival_jdis(i,j)\),若节点\(i\)的点权的变化量为\(\Delta v\),那么\(\Delta W=\Delta v\sum_{j=1}^{n}val_jdis(i,j)\),相当于\(\Delta v*calc(i)\),所以说一样地计算即可。
所以最终询问的答案就是:
\[\sum_{i=1}^{n}s_i^2=Sum*\sum_{i=1}^{n}s_i-W=Sum(calc(i)+Sum)-W\]

code

#include<cstdio>
#include<algorithm>
using namespace std;
#define ll long long
const int N = 200005;
int gi()
{
    int x=0,w=1;char ch=getchar();
    while ((ch<'0'||ch>'9')&&ch!='-') ch=getchar();
    if (ch=='-') w=0,ch=getchar();
    while (ch>='0'&&ch<='9') x=(x<<3)+(x<<1)+ch-'0',ch=getchar();
    return w?x:-x;
}
struct edge{int to,next;}a[N<<1];
int n,q,val[N],head[N],cnt,pa[N],dep[N],sz[N],son[N],top[N];
void dfs1(int u,int f)
{
    pa[u]=f;dep[u]=dep[f]+1;sz[u]=1;
    for (int e=head[u];e;e=a[e].next)
    {
        int v=a[e].to;if (v==f) continue;
        dfs1(v,u);
        sz[u]+=sz[v];if (sz[v]>sz[son[u]]) son[u]=v;
    }
}
void dfs2(int u,int f)
{
    top[u]=f;
    if (son[u]) dfs2(son[u],f);else return;
    for (int e=head[u];e;e=a[e].next)
        if (a[e].to!=pa[u]&&a[e].to!=son[u])
            dfs2(a[e].to,a[e].to);
}
int lca(int u,int v)
{
    while (top[u]^top[v])
    {
        if (dep[top[u]]<dep[top[v]]) swap(u,v);
        u=pa[top[u]];
    }
    return dep[u]<dep[v]?u:v;
}
int getdis(int u,int v){return dep[u]+dep[v]-(dep[lca(u,v)]<<1);}
int tot,root,vis[N],w[N],fa[N];
ll sum[N],gather[N],tofa[N],sigma,omega,ans;
void getroot(int u,int f)
{
    sz[u]=1;w[u]=0;
    for (int e=head[u];e;e=a[e].next)
    {
        int v=a[e].to;if (v==f||vis[v]) continue;
        getroot(v,u);
        sz[u]+=sz[v];w[u]=max(w[u],sz[v]);
    }
    w[u]=max(w[u],tot-sz[u]);
    if (w[u]<w[root]) root=u;
}
void solve(int u,int f)
{
    fa[u]=f;vis[u]=1;
    for (int e=head[u];e;e=a[e].next)
    {
        int v=a[e].to;if (vis[v]) continue;
        tot=sz[v];
        root=0;
        getroot(v,0);
        solve(root,u);
    }
}
void modify(int u,int v)
{
    sum[u]+=v;
    for (int i=u;fa[i];i=fa[i])
    {
        int dist=getdis(u,fa[i]);
        sum[fa[i]]+=v;
        gather[fa[i]]+=dist*v;
        tofa[i]+=dist*v;
    }
}
ll calc(int u)
{
    ll res=gather[u];
    for (int i=u;fa[i];i=fa[i])
    {
        int dist=getdis(u,fa[i]);
        res+=(ll)dist*(sum[fa[i]]-sum[i]);
        res+=gather[fa[i]]-tofa[i];
    }
    return res;
}
void DP(int u)
{
    sz[u]=val[u];
    for (int e=head[u];e;e=a[e].next)
    {
        int v=a[e].to;if (v==pa[u]) continue;
        DP(v);sz[u]+=sz[v];
    }
    omega+=1ll*sz[u]*(sigma-sz[u]);
}
int main()
{
    n=gi();q=gi();
    for (int i=1;i<n;i++)
    {
        int u=gi(),v=gi();
        a[++cnt]=(edge){v,head[u]};head[u]=cnt;
        a[++cnt]=(edge){u,head[v]};head[v]=cnt;
    }
    dfs1(1,0);dfs2(1,1);
    tot=w[0]=n;
    getroot(1,0);
    solve(root,0);
    for (int i=1;i<=n;i++)
        val[i]=gi(),modify(i,val[i]),sigma+=val[i];
    DP(1);
    while (q--)
    {
        int opt=gi(),x=gi();
        if (opt==1)
        {
            int y=gi();
            modify(x,y-val[x]);sigma+=y-val[x];
            omega+=(y-val[x])*calc(x);
            val[x]=y;
        }
        else printf("%lld\n",(calc(x)+sigma)*sigma-omega);
    }
    return 0;
}

原文地址:https://www.cnblogs.com/zhoushuyu/p/8309410.html

时间: 2024-10-12 08:25:52

[Luogu3676]小清新数据结构题的相关文章

Luogu3676 小清新数据结构题 动态点分治

传送门 换根类型的统计问题动态点分治都是很好做的. 设所有点的点权和为$sum$ 首先,我们先不考虑求$\sum\limits_i s_i^2$,先考虑如何在换根的情况下求$\sum\limits_i s_i$. 考虑一个点$i$会被统计多少次,显然是$dep_i+1$,那么$\sum\limits_i s_i = \sum\limits_i (dep_i+1) \times val_i = \sum\limits_i dep_i \times val_i + sum$. $\sum\limit

【luoguP3676】 小清新数据结构题 推式子(LCT维护)

挺有趣的一个数据结构题,不必用 LCT 维护,只不过 LCT 比较好写 ~ code: #include <cstdio> #include <string> #include <cstring> #include <algorithm> #define N 200008 #define ll long long #define lson s[x].ch[0] #define rson s[x].ch[1] using namespace std; ll a

【刷题】洛谷 P3676 小清新数据结构题

题目背景 本题时限2s,内存限制256M 题目描述 在很久很久以前,有一棵n个点的树,每个点有一个点权. 现在有q次操作,每次操作是修改一个点的点权或指定一个点,询问以这个点为根时每棵子树点权和的平方和. (题目不是很好懂,没看太懂的可以看看样例解释) 输入输出格式 输入格式: 第一行两个整数n.q. 接下来n-1行每行两个整数a和b,表示树中a与b之间有一条边,保证给出的边不会重复. 接下来一行n个整数,第i个整数表示第i个点的点权. 接下来q行每行两或三个数,如果第一个数为1,那么接下来有两

Luogu 3676 小清新数据结构题

推荐博客: http://www.cnblogs.com/Mychael/p/9257242.html 感觉还挺好玩的 首先考虑以1为根,把每一个点子树的权值和都算出来,记为$val_{i}$,那么在所有操作都没有开始的时候(以$1$为根的)$ans_{1} = \sum_{i= 1}^{n}val_{i}^{2}$ 考虑到一个修改的操作只会对修改的点$x$到根($1$)链上的点产生影响,那么一次修改只要修对这条树链上的点增加$v - a_{x}$(假设修改后的值为$v$)就好了. 链剖之后线段

lp3676 小清新数据结构题

传送门 Description 有一棵\(n\)个点的树,每个点有一个点权. 现在有\(q\)次操作,每次操作是修改一个点的点权或指定一个点,询问以这个点为根时每棵子树点权和的平方和. Solution 我们设\(Sum=\sum_{i=1}^{n} w_i\),\(s_i\)表示\(i\)子树的权值和 发现不管根是哪个节点,\(W=\sum_{i=1}^n s_i(Sum-s_i)\)都是一个定值 因为它相当于对于每条边连接的两个联通块的"点权和的积"的和 所以,我们要求的\(\su

小清新签到题(DP)

传送门 然鹅我并不觉得这道题很清新rua 思维巧妙!(参考) 对于第k小,我们可以这样考虑,若是第k小,那么比它小的方案应该是有k-1个. 在排列组合中,若固定i放在j位置,方案数是确定的,即:i固定在j位置,满足这个条件的序列的rank是在一个范围内的. 对于逆序对 常见思考方式是从小到大枚举数字,考虑对逆序对个数做出的贡献(从小到大插入,后插入不会对前插入造成影响) 设f[i][j]为i个数,j个逆序对的方案数,可得转移方程为 f[i][j]=f[i-1][j]+f[i-1][j-1]+……

绪论-第1章-《数据结构题集》习题解析-严蔚敏吴伟民版

习题集解析部分 第1章  绪论 ——<数据结构题集>-严蔚敏.吴伟民版        源码使用说明  链接??? <数据结构-C语言版>(严蔚敏,吴伟民版)课本源码+习题集解析使用说明        课本源码合辑  链接??? <数据结构>课本源码合辑        习题集全解析  链接??? <数据结构题集>习题解析合辑          本习题文档的存放目录:数据结构\▼配套习题解析\▼01 绪论\        文档中源码的存放目录:数据结构\▼配套习

查找-第9章-《数据结构题集》习题解析-严蔚敏吴伟民版

习题集解析部分 第9章 查找 ——<数据结构题集>-严蔚敏.吴伟民版        源码使用说明  链接??? <数据结构-C语言版>(严蔚敏,吴伟民版)课本源码+习题集解析使用说明        课本源码合辑  链接??? <数据结构>课本源码合辑        习题集全解析  链接??? <数据结构题集>习题解析合辑       相关测试数据下载  链接? 数据包       本习题文档的存放目录:数据结构\▼配套习题解析\▼09 查找       文档

LBS(位置定位服务)发布小清新版本

web前端对我来说始终是一个短板,当然在不追求全栈的情况下,我也觉得没必要多精通,至少对于平常调整已经比较熟悉了.但是这次因为将lbs的页面外包给别人,而因为各种各样的原因导致交付时间一拖再拖,让人非常失望,大感掌握前端的必要性.总算在前几天交付了,使用的是bootstrap,虽然这个前端框架之前就又耳闻,但是因为公司一直使用ext,也没有涉略.这次没办法,已经答应了合作伙伴在4月份之前搞出来,所以上一周边学bootstrap边做,现在发现bootstrap就是我之前一直想找的框架.为什么这么说