若至错误,玄二关求调
查看原帖
若至错误,玄二关求调
654577
RainySoul楼主2025/1/17 09:16
#include<bits/stdc++.h>
#define int long long
using namespace std;
const int N=100010;
inline int read(){
    int x=0,f=1;
    char c=getchar();
    while(c<'0'||c>'9'){
        if(c=='-')f=-1;
        c=getchar();
    }
    while(c>='0'&&c<='9'){
        x=x*10+c-'0';
        c=getchar();
    }
    return x*f;
}
struct zyx{
    int to,w;
};
vector<zyx> e[N];
int dfn[N],rnk[N],dis[N],sz[N],fz[N],son[N],top[N];
int cnt,n,Q;
struct SegementTree{
    int w[N<<2];//w叶子记录子树大小,维护的是子树大小max
    int sum[N<<2],lazy[N<<2];
    int edge[N<<2];//记录边权的
    void pushup(int u){
        w[u]=max(w[u*2],w[u*2+1]);
        sum[u]=sum[u*2]+sum[u*2+1];
    }
    void pushdown(int u){
        if(!lazy[u])return;
        w[u*2]+=lazy[u];
        w[u*2+1]+=lazy[u];
        lazy[u*2]+=lazy[u];
        lazy[u*2+1]+=lazy[u];
        sum[u*2]+=lazy[u]*edge[u*2];
        sum[u*2+1]+=lazy[u]*edge[u*2+1];
        lazy[u]=0;
    }
    void build(int u,int l,int r){
        if(l==r){
            edge[u]=dis[rnk[l]]-dis[fz[rnk[l]]];
            w[u]=0;
            return;
        }
        int mid=(l+r)>>1;
        build(u*2,l,mid);
        build(u*2+1,mid+1,r);
        edge[u]=edge[u*2]+edge[u*2+1];
        //这里难道需要pushup edge 吗?
    }
    void add(int u,int l,int r,int x,int y,int k){
        if(x<=l&&r<=y){
            w[u]+=k;
            sum[u]+=k*edge[u];
            lazy[u]+=k;
            return;
        }
        pushdown(u);
        int mid=(l+r)>>1;
        if(x<=mid)add(u*2,l,mid,x,y,k);
        else add(u*2+1,mid+1,r,x,y,k);
        pushup(u);
    }
    int query(int u,int l,int r,int x,int y){
        if(x<=l&&r<=y){
            return sum[u];
        }
        int mid=(l+r)>>1;
        pushdown(u);
        int res=0;
        if(x<=mid)res+=query(u*2,l,mid,x,y);
        if(y>mid)res+=query(u*2+1,mid+1,r,x,y);
        return res;
    }
    int search(){
        int u=1,l=1,r=n;
        while(l<r){
            int mid=(l+r)>>1;
            pushdown(u);
            if(w[u*2+1]*2>=w[1])u=u*2+1,l=mid+1;
            else u=u*2,r=mid;
        }
        return rnk[l];
    }
    //上面一部分是线段树上二分求重心
    //下面一部分需要实现的是
    //区间加,区间 a*b 的和,其中 只改变 a
    void ADD(int now,int e){
        while(now!=0){
            add(1,1,n,dfn[top[now]],dfn[now],e);
            now=fz[top[now]];
        }
        return;
    }
    int QUE(int now){
        int res=0;
        while(now!=0){
            res+=query(1,1,n,dfn[top[now]],dfn[now]);
            now=fz[top[now]];
        }
        return res;
    }
}T;
void dfs1(int now,int fa){
    sz[now]=1;
    fz[now]=fa;
    for(int i=0;i<(int)e[now].size();i++){
        int to=e[now][i].to;
        if(to==fa)continue;
        dis[to]=dis[now]+e[now][i].w;
        dfs1(to,now);
        sz[now]+=sz[to];
        if(sz[son[now]]<sz[to])son[now]=to;
    }
}
void dfs2(int now,int topf){
    dfn[now]=++cnt;
    rnk[cnt]=now;
    top[now]=topf;
    if(!son[now])return;
    dfs2(son[now],topf);
    for(int i=0;i<(int)e[now].size();i++){
        int to=e[now][i].to;
        if(to==son[now]||to==fz[now])continue;
        dfs2(to,to);
    }
}
signed main(){
    ios::sync_with_stdio(0);
    cin.tie(0);cout.tie(0);
    n=read(),Q=read();
    for(int i=1;i<n;i++){
        int u=read(),v=read(),w=read();
        e[u].push_back((zyx){v,w});
        e[v].push_back((zyx){u,w});
    }
    dfs1(1,0);
    dfs2(1,1);
    T.build(1,1,n);
    int dis_y_root=0,sum_e_y=0;
    for(int i=1;i<=Q;i++){
        int u=read(),e=read();
        T.ADD(u,e);
        dis_y_root+=e*dis[u];
        sum_e_y+=e;
        int zx=T.search();
        // cout<<"重心为"<<zx<<'\n';
        // cout<<"第一项为"<<dis[zx]*sum_e_y<<'\n';
        // cout<<"第二项为"<<dis_y_root<<'\n';
        // cout<<"QUE="<<T.QUE(zx)<<'\n';
        cout<<dis[zx]*sum_e_y+dis_y_root-2*T.QUE(zx)<<'\n';
    }
    return 0;
}

思路是区间 max\text{max} 线段树上二分求重心,叶子节点记录子树大小。后面树剖维护答案。

2025/1/17 09:16
加载中...