FZU2277 Change(dfs序+树状数组)

传送门

题意

q次操作,操作有两种:
1 v x k:a[v]+=x,a[v']+=x-k(v'是v的子节点)...
2 v:查询\(a[v]mod(10^9+7)\)

分析

子节点增加的值为\(x+dep[v]*k-dep[s]*k\),那么维护两个值x+dep[v]*k与-k,用两个树状数组维护这两个值,做到区间修改,单点查询。在处理之前需要处理出该树的dfs序,具体见代码

trick

1.注意爆int

代码

#include <cstdio>
#include <cstring>
#include <vector>
#include <algorithm>
using namespace std;

#define ll long long
#define F(i,a,b) for(int i=a;i<=b;++i)
#define R(i,a,b) for(int i=a;i<b;++i)
const int mod = 1e9+7;
const int N = 300300;

int l[N],r[N],dep[N];
int a[N],b[N];
int t,n,q,judge,vv,x,k;
int loc;
vector<int>v[N];

void dfs(int u,int depth)//处理出dfs序与每个点的深度
{
   dep[u]=depth;
   l[u]=(++loc);
   int sz=v[u].size();
   R(i,0,sz)
   {
      dfs(v[u][i],depth+1);
   }
   r[u]=loc;
}
//差分思想
void adda(int x,int y)//维护x+dep[v]*k
{
   for(int i=x;i<=n;i+=(i&-i)) a[i]=(a[i]+y)%mod;
}

void addb(int x,int y)维护-k
{
   for(int i=x;i<=n;i+=(i&-i)) b[i]=(b[i]+y)%mod;
}

int sum(int x,int y)//单点查询,仔细思考
{
   int ret=0,ans=0;
   for(int i=x;i;i-=(i&-i)) ret=(ret*1LL+a[i])%mod;
   for(int i=x;i;i-=(i&-i)) ans=(ans*1LL+b[i])%mod;
   ans=ans*1LL*dep[y]%mod;
   return (ret+ans)%mod;
}

int main()
{
   for(scanf("%d",&t);t--;)
   {
      scanf("%d",&n);
      F(i,2,n)
      {
         scanf("%d",&x);
         v[x].push_back(i);
      }
      loc=0;
      dfs(1,1);
      F(i,0,n) a[i]=b[i]=0;
      for(scanf("%d",&q);q--;)
      {
         scanf("%d %d",&judge,&vv);
         if(judge==1)
         {
            scanf("%d %d",&x,&k);
            int ret=(x+dep[vv]*1LL*k)%mod;
            adda(l[vv],ret);
            adda(r[vv]+1,-ret+mod);
            addb(l[vv],(-k+mod)%mod);
            addb(r[vv]+1,k%mod);
         }
         else
         {
            printf("%d\n",sum(l[vv],vv));
         }
      }
      F(i,1,n) v[i].clear();
   }
}
posted @ 2017-07-23 15:20  遗风忘语  阅读(304)  评论(0编辑  收藏  举报