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)\)都是一个定值

因为它相当于对于每条边连接的两个联通块的”点权和的积“的和

所以,我们要求的\(\sum_{i=1}^{n} s_i^2=Sum*(\sum_{i=1}^n s_i)-W\)

考虑怎么计算\(calc(root)=\sum_{i=1}^{n} s_i\)

发现其实上就是\(Sum+\sum_{i=1}^n dis(root,i)*val_i\)

可以用点分树来维护

考虑怎么计算\(W\)

对于每次修改\(val_i+=delta\),就有\(W+=\sum_{j=1}^n val_j*dis(i,j)*delta=delta*calc(i)\)

发现这道题本质和幻想乡战略游戏是一样的

可是把原来的代码交上去,就\(TLE\)了,只好考虑重构

不妨把过程重新理一遍:

  1. 点分树有一个性质,对于树上的两个点,它们的\(lca\)一定在两点在原树上的简单路径上

    因此,要求两个点的实际距离,可以通过分别计算它们到\(lca\)的实际距离求和得到

  2. 考虑以上性质,我们可以设:
    • \(vsum_i\)表示\(i\)在点分树上的子树内的权值和
    • \(dis1_i\)表示 \(\sum_{j} dis(i,j)*val_j\),\(j\)是\(i\)在点分树上的子树内的节点
    • \(dis2_i\)表示 \(\sum_{j} dis(par_i,j)*val_j\)
    • \(par_i\)是\(i\)在点分树上的父亲
  3. \(dis1_i=\sum_{son} dis2_son\),\(son\)是\(i\)在点分树上的儿子
  4. \(calc(i)\)其实上就是枚举\(lca\)

    \(lca=i\)时,和为\(dis1_i\)

    \((\sum_{lca=par_j} dis1_{par_j}-dis2_j)\)算的是外围节点到\(lca\)的和

    \(dis(i,par_j)*(vsum_{par_j}-vsum_j))\)算的是它们到\(i\)的和

我们发现,求距离的部分其实不需要像之前那样全部记下来,可以用\(RMQ\)求树上距离

具体来说,和\(RMQ\)求\(LCA\)差不多,只不过维护的最小值不是欧拉序,而是到根路径的长

Code?

#include<bits/stdc++.h>
#define ll long long
#define max(a,b) ((a)>(b)?(a):(b))
#define min(a,b) ((a)<(b)?(a):(b))
#define reg register
#define int ll

inline int read()
{
    int x=0,f=1;char ch=getchar();
    while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}
    while(ch>='0'&&ch<='9'){x=(x<<3)+(x<<1)+ch-'0';ch=getchar();}
    return x*f;
}
const int MN=2e5+5;

int n,m;
struct edge{int to,w,nex;}e[MN<<2];int en,hr[MN];
void ins(int x,int y,int w,int *h){e[++en]=(edge){y,w,h[x]};h[x]=en;}
int Val[MN],O,SUM;

struct Tree
{
    int Hr[MN],ind,st[MN<<2][21],dep[MN],pos[MN<<1],lg[MN<<2],_siz[MN];

    inline void Ins(int x,int y,int w){ins(x,y,w,Hr);ins(y,x,w,Hr);}

    int dis(int x,int y)
    {
        if(pos[x]>pos[y]) std::swap(x,y);
        reg int k=lg[pos[y]-pos[x]+1];
        return dep[x]+dep[y]-2*min(st[pos[x]][k],st[pos[y]-(1<<k)+1][k]);
    }
    void dfs(int x,int fa=0)
    {
        st[pos[x]=++ind][0]=dep[x];reg int i;
        for(i=Hr[x];i;i=e[i].nex)if(e[i].to^fa)
            dep[e[i].to]=dep[x]+e[i].w,dfs(e[i].to,x),st[++ind][0]=dep[x];
    }
    void get_O(int x=1,int fa=0)
    {
        reg int i;_siz[x]=Val[x];
        for(i=Hr[x];i;i=e[i].nex)if(e[i].to^fa)
            get_O(e[i].to,x),_siz[x]+=_siz[e[i].to];
        O+=1ll*(SUM-_siz[x])*_siz[x];
    }
    inline void pre_work()
    {
        reg int i,j;get_O();dfs(1);
        for(lg[0]=-1,i=1;i<(MN<<2);i++)lg[i]=lg[i>>1]+1;
        for(j=1;j<20;++j)for(i=1;i+(1<<j)-1<=ind&&i<=ind;++i)
        st[i][j]=min(st[i][j-1],st[i+(1<<(j-1))][j-1]);
    }

}T;

int sum,rt,mx[MN],vis[MN],par[MN],cnt,siz[MN];
ll dis1[MN],dis2[MN],sumv[MN];

void getrt(int x,int fa)
{
    siz[x]=1;mx[x]=0;reg int i;
    for(i=T.Hr[x];i;i=e[i].nex)if(!vis[e[i].to]&&(e[i].to!=fa))
        getrt(e[i].to,x),siz[x]+=siz[e[i].to],mx[x]=max(mx[x],siz[e[i].to]);
    mx[x]=max(mx[x],sum-siz[x]);
    if(mx[x]<mx[rt]) rt=x;
}

void _work(int x,int fa)
{
    vis[x]=1;par[x]=fa;reg int i;
    for(i=T.Hr[x];i;i=e[i].nex)if(!vis[e[i].to])
        mx[rt=0]=sum=siz[e[i].to],getrt(e[i].to,0),ins(x,rt,0,hr),_work(rt,x);
}

void pre_work()
{
    sum=mx[rt=0]=n;getrt(1,0);
    int tmp=rt;_work(rt,0);rt=tmp;
}

inline void Modify(int x,int val)
{
    sumv[x]+=val;reg int i,dis;
    for(i=x;par[i];i=par[i])
    {
        dis=T.dis(par[i],x);
        dis1[par[i]]+=dis*val;
        dis2[i]+=dis*val;
        sumv[par[i]]+=val;
    }
}

inline ll calc(int x)
{
    ll ans=dis1[x];reg int i,dis;
    for(i=x;par[i];i=par[i])
    {
        dis=T.dis(par[i],x);
        ans+=dis1[par[i]]-dis2[i];
        ans+=dis*(sumv[par[i]]-sumv[i]);
    }
    return ans;
}

signed main()
{
    n=read();m=read();
    reg int i,x,y;
    for(i=1;i<n;i++) x=read(),y=read(),T.Ins(x,y,1);
    pre_work();
    for(i=1;i<=n;++i) SUM+=Val[i]=read();
    T.pre_work();
    for(i=1;i<=n;++i) Modify(i,Val[i]);

    while(m--)
    {
        reg int opt=read();
        if(opt==1)
        {
            x=read();y=read()-Val[x];Modify(x,y);
            SUM+=y;O+=y*calc(x);Val[x]+=y;
        }
        else printf("%lld\n",SUM*(calc(read())+SUM)-O);
    }
    return 0;
}


Blog来自PaperCloud,未经允许,请勿转载,TKS!

原文地址:https://www.cnblogs.com/PaperCloud/p/10664448.html

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

lp3676 小清新数据结构题的相关文章

【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

[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\). 我们先看\(

【刷题】洛谷 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$)就好了. 链剖之后线段

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

小清新签到题(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就是我之前一直想找的框架.为什么这么说