洛谷 小清新数据结构题

3 篇文章 0 订阅
2 篇文章 0 订阅

前言

标算点分治,蒟蒻树链剖分。

题面

原题可以。由于表达能力过弱,所以自己不再复述题面。

sol

可能有一大堆 Σ Σ 。。
然而我并不会那么数学的符号。。。
所以全凭脑算。
在不考虑换根与修改的情况下,只要理解了题意,求解就是dfs。但由于发现修改是一个点一个点修改的,所以每次要针对一个点的修改来算出与修改前的贡献差。所以说要针对出一个点来算贡献。如果我们是按一个一个点来算贡献的话,就可以通过枚举他所在的子树来算贡献。发现了一个平方,手推式子不可避免。
如果一个点改变了,考虑他所在的一个子树,把平方式子拆开,就形如 a2+b2+c2+...+2ab+2bc+2ac+... a 2 + b 2 + c 2 + . . . + 2 a b + 2 b c + 2 a c + . . . 显然有的与a(假设a发生变化)无关的式子,这些式子的值肯定不变.除了一个平方项,其他都可以提一个 2a 2 a 出来,剩下的就是一个求和。这样针对每一个点,在以1为根的情况下,维护他的子树的权值和(包括自己)。这样,用线段树(或者树状数组),求出每一个包括a的子树的权值和减去几个a再乘一个 2a 2 a .
如果根变了,那么,先设现在的根为 u u ,设a1,a2...为在以1为根的情况下,子树i的权值和为 ai a i b1,b2... b 1 , b 2 . . . 是以 u u 为根的情况。以1为根的答案为ans,u为根的情况答案为 Ans A n s ,然后推一波式子,主要是要把 bi b i 消掉,因为没有维护 bi b i .

code

#include<bits/stdc++.h>
#define LL long long
using namespace std;
namespace zjy_io{
    inline char gc(){
        static char buf[1<<6],*p1=buf,*p2=buf;
        return (p1==p2)&&(p2=(p1=buf)+fread(buf,1,1<<6,stdin),p1==p2)?EOF:*p1++;
    }
    template <class T>
    inline void read(T&data){
        data=0;register int caa=1;register char ch=0;
        while((ch!='-')&&(ch<'0'||ch>'9'))ch=gc();
        ch=='-'?caa=-1,ch=gc():caa=1;
        while(ch<='9'&&ch>='0'){
            data=(data<<3)+(data<<1)+(ch^48);
            ch=gc();
        }
        data=data*caa;
    }
}
using namespace zjy_io;
const int _ = 2e5+1e2;
struct edge{
    int to,nt;
}e[_<<1];
int s[_],head[_],n,q,deep[_],top[_],son[_],size[_],fa[_],dfn[_],fdfn[_],cnt;
LL sum[_],tree[_<<2],lazy[_<<2];
LL ans1,ALL;
inline void update(register int qp){
    tree[qp]=tree[qp<<1]+tree[qp<<1|1];return;
}
inline void pushdown(register int qp,register int le,register int ri){
    if(lazy[qp]){
        register int mid= (le+ri)>>1,len1=mid+1-le,len2=ri-mid;
        lazy[qp<<1]+=lazy[qp],tree[qp<<1]+=lazy[qp]*len1;
        lazy[qp<<1|1]+=lazy[qp],tree[qp<<1|1]+=lazy[qp]*len2;
        lazy[qp]=0; 
    }
    return;
}
void modify(const int l,const int r,register LL zh,int L,int R,register int qp){
    if(L>=l&&R<=r){tree[qp]+=zh*(R-L+1);lazy[qp]+=zh;return;}
    register int mid=(L+R)>>1;
    if(l<=mid)modify(l,r,zh,L,mid,qp<<1);
    if(r>mid)modify(l,r,zh,mid+1,R,qp<<1|1);
    update(qp);return;
}
LL Query(const int l,const int r,register int L,register int R,int qp){
    if(L>=l&&R<=r){
        return tree[qp];
    }
    pushdown(qp,L,R);
    register int mid= (L+R)>>1;
    register LL kkk=0;
    if(l<=mid)kkk+=Query(l,r,L,mid,qp<<1);
    if(r>mid)return kkk+Query(l,r,mid+1,R,qp<<1|1);
    return kkk;
}
inline LL pre_query(register int a){
    if(a==1)return ans1;
//int gnt=0;
    register LL delta=0;
    register int Begin=a,End;
    while(Begin){
        /*if(a==3){++gnt;if(gnt>5)exit(0);
        cout<<Begin<<endl;}*/
        End=top[Begin];
        delta=delta+2LL*Query(dfn[End],dfn[Begin],1,n,1)*ALL;
        Begin=fa[End];
    }
    delta=1LL*(deep[a]-deep[1]+2)*(ALL)*(ALL)-delta;
    return ans1+delta;
}
void add(register int a,register int b){e[++cnt].to=a,e[cnt].nt=head[b],head[b]=cnt;}
inline void pre_fix(register int a,register int b){
    register LL delta=0;
    register int Pre=s[a],Now=b;s[a]=b;ALL=ALL+Now-Pre;
    delta=delta+1LL*deep[a]*(1LL*Now*Now-1LL*Pre*Pre);
    register int Begin=a;
    while(Begin){

        register int End=top[Begin];
        register LL dist = ( Query(dfn[End],dfn[Begin],1,n,1) - (deep[Begin]-deep[End]+1)*Pre)*(Now-Pre)*2;
        delta=delta+dist;
        modify(dfn[End],dfn[Begin],Now-Pre,1,n,1);
        Begin=fa[End];
    }
    ans1+=delta;
}
void dfsI(register int now,register int ff){
    fa[now]=ff,deep[now]=deep[ff]+1,sum[now]=s[now];
    size[now]=1;register int mx=0;
    for(register int i=head[now];i;i=e[i].nt){
        if(e[i].to==ff)continue;
        dfsI(e[i].to,now);
        size[now]+=size[e[i].to];
        if(mx<size[e[i].to])mx=size[e[i].to],son[now]=e[i].to;
        sum[now]+=sum[e[i].to];
    }
    ans1=ans1+sum[now]*sum[now];
    return;
}
void dfsII(register int now,register int topf){
    dfn[now]=++cnt,fdfn[cnt]=now;
    top[now]=topf;
    modify(cnt,cnt,sum[now],1,n,1);
    if(son[now])
        dfsII(son[now],topf);
    for(register int i=head[now];i;i=e[i].nt){
        if(e[i].to==fa[now]||e[i].to==son[now])continue;
        dfsII(e[i].to,e[i].to);
    }
    return;
}
int main(){
    read(n);read(q);
    for(register int i=1,a,b;i<n;++i)read(a),read(b),add(a,b),add(b,a);
    for(register int i=1;i<=n;++i)read(s[i]),ALL+=s[i];
    dfsI(1,0);cnt=0;
    dfsII(1,1);
    while(q--){
        register int ki,a,b;
        read(ki);
        if(ki==1){
            read(a),read(b);
            pre_fix(a,b);
        }
        else {
            read(a);
            printf("%lld\n",pre_query(a));
        }
    }
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
洛谷P2791是一道关于幼儿园篮球的目。目描述如下: 在幼儿园里,小朋友们正在进行篮球比赛。每个小朋友都有一个篮球,他们按照顺序依次投篮。每个小朋友投篮时,可以选择将篮球投给左边的小朋友或者右边的小朋友。每个小朋友投篮得分的规则如下: 1. 如果一个小朋友左右两边的小朋友都没有投篮过,那么他的得分为1; 2. 如果一个小朋友左边的小朋友投篮过,但右边的小朋友没有投篮过,那么他的得分为左边小朋友的得分加1; 3. 如果一个小朋友右边的小朋友投篮过,但左边的小朋友没有投篮过,那么他的得分为右边小朋友的得分加1; 4. 如果一个小朋友左右两边的小朋友都投篮过,那么他的得分为左边小朋友和右边小朋友得分的最大值加1。 现在给定每个小朋友投篮的顺序,请你计算每个小朋友的得分。 例如,给定投篮顺序为[1, 0, 1, 0, 1],则第一个小朋友的得分为1,第二个小朋友的得分为2,第三个小朋友的得分为1,第四个小朋友的得分为2,第五个小朋友的得分为1。 你可以通过编写程序来解决这个问。具体的解思路可以参考以下步骤: 1. 创建一个数组scores,用来存储每个小朋友的得分; 2. 遍历投篮顺序数组,对于每个小朋友,根据上述规则计算他的得分,并将得分存入scores数组中; 3. 最后输出scores数组即可。 希望以上解答对你有帮助!如果你还有其他问,请继续提问。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值