Change FZU - 2277 【dfs序】【树状数组】

16 篇文章 0 订阅
6 篇文章 0 订阅

题目链接:https://cn.vjudge.net/problem/FZU-2277

题意:

1 v x k : a[v]+=x , a[v’]+=x-k (v’ is child of v) , a[v’’]+=x-2*k (v’’ is child of v’) and so on.

2 v : Output a[v] mod 1000000007(10^9 + 7).

 

错误思路:

我们把点权分成两部分,一部分是x,一部分是k。那么用dfs序可以解决x的修改,然后发现k值修改很难维护,因为涉及到了深度差,子树的各点距根不同,所以增加的值也不同,不好直接进行区间修改。

想了很长时间只想到一个O(n*(层数))的做法,dfs序区间修改解决x。对于k值,当查询一个点v时,就往上走到根,判断它的祖先是否被1操作修改了,如果被修改了,那么根据层数差 和 修改的值来更新v的值。

这样的话数据随机生成的话才能通过。

 

正确思路:

将修改的子树根记录为 u ,子树上的点记录为v。记s[i]为第i个点的点权。

那么每次修改可以表示为:

          s[v] += x - (dis[v]-dis[u])*k;

     既 s[v] += x+dis[u]*k   - dis[v] * k;

然后可以发现,对于确定的子树而言x+dis[u]*k是一个常数,可以用区间修改(树状数组差分)处理。

对于后半部分,k值也可以用另一个树状数组处理,然后统计的时候再乘上当前点的深度就可以了。

比赛的时候两部分的值考虑到分开处理了,但是并没有尝试对式子进行变形。

#include <algorithm>
#include <iostream>
#include <cstring>
#include <vector>
//#include <bits/stdc++.h>
#define rep(i, a, b) for(int i = (a);i <= (b); i++)
#define mp make_pair
#define pb push_back
#define ll long long
#define pli pair<long long,int>
#define all(x) (x).begin(),(x).end()
using namespace std;
const int mod = 1e9+7;
const int N = 3e5+1000;
vector<int>nxt[N];
int in[N],out[N],tot;
ll tree[N],tree2[N],dis[N];
int lb(int x) {
    return x&-x;
}
void add(ll* tree,int x,ll data) {
    data = ((data%mod)+mod)%mod;
    while(x<N-500) {
        tree[x] += data;
        x+=lb(x);
    }
}
ll que(ll *tree,int x) {
    ll ans = 0;
    while(x>0) {
        ans += tree[x];
        ans %= mod;
        x -= lb(x);
    }
    return ans;
}
void dfs(int u) {
    in[u] = ++tot;
    int siz = nxt[u].size();
    rep(i, 0, siz-1) {
        int v = nxt[u][i];
        dis[v] = dis[u]+1;
        dfs(v);
    }
    out[u] = tot;
}
void init(int n) {
    tot = 0;
    rep(i, 1, n) nxt[i].clear();
    dis[1] = 0;
    memset(tree,0,sizeof(tree));
    memset(tree2,0,sizeof(tree2));
}
int main() {
    //freopen("a.txt","r",stdin);
    ios::sync_with_stdio(0);
    int T;
    cin>>T;
    while(T--) {
        int n;
        cin>>n;
        init(n);
        rep(i, 2, n) {
            int x;
            cin>>x;
            nxt[x].pb(i);
        }
        dfs(1);
        int q;
        cin>>q;
        rep(i, 1, q) {
            int oper, u;
            cin>>oper;
            if(oper==1) {
                ll x,k;
                cin>>u>>x>>k;
                add(tree,in[u],x+dis[u]*k);
                add(tree,out[u]+1,-dis[u]*k-x);
                add(tree2,in[u],-k);
                add(tree2,out[u]+1,k);
            }
            else {
                cin>>u;
                ll ans = que(tree,in[u]);
                ans += que(tree2,in[u])*dis[u];
                ans = ((ans%mod)+mod)%mod;
                cout<<ans<<endl;
            }
        }
    }
    return 0;
}

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值