P10107 题解
考虑按位计算贡献,先考虑链的情况。考虑倍增,设
考虑把这东西搬到树上,但由于子树内在某一层上的点不止一个,只能在
那么先把
那么问题就在于还需要一个记录贡献变化量的数组
所有的转移和统计答案都跟链上类似,把每个询问扔到对应节点上处理即可。只是要格外注意在
#include<iostream>
#include<vector>
#define pii pair<int,int>
#define ll long long
using namespace std;
const int N=1e6+10;
const int K=20+3;
int n,q,v[N],d[N],son[N]; ll res[N];
vector <int> e[N];
vector <pii> a[N];
struct nod{ll f[K],g[K];}temp[N],*f[N],*cur=temp;
void dfs(int u)
{
d[u]=0;
for(int v:e[u])
{
dfs(v),d[u]=max(d[u],d[v]);
if(d[v]>d[son[u]]) son[u]=v;
}
d[u]++;
}
void merg(int u,int v)
{
for(int j=0;j<20;j++) f[u][0].g[j]+=f[v][0].g[j];
ll s=0;
for(int p=0,k=1;k<20;p+=(1<<(k-1)),k++)
{
if(p<d[v]) s+=f[v][p].f[k-1]+(1<<(k-1))*f[v][p].g[k-1];
if(p+(1<<(k-1))<d[v]) s-=(1<<(k-1))*f[v][p+(1<<(k-1))].g[k-1];
f[u][0].f[k]+=s;
}
}
void dfsb(int u)
{
for(int j=0;j<20;j++) f[u][0].f[j]=v[u],f[u][0].g[j]=(((v[u]>>j)&1)?-1:1);
if(son[u]) f[son[u]]=f[u]+1,dfsb(son[u]),merg(u,son[u]);
for(int v:e[u]) if(v!=son[u])
{
f[v]=cur,cur+=d[v],dfsb(v),merg(u,v);
for(int j=0;j<d[v];j++) for(int k=0;k<20;k++)
f[u][j+1].f[k]+=f[v][j].f[k],f[u][j+1].g[k]+=f[v][j].g[k];
}
for(pii te:a[u])
{
int x=min(te.first,d[u]-1),id=te.second;
for(int p=0,k=19;k>=0;k--)
{
if(p+(1<<k)-1>x) continue;
res[id]+=f[u][p].f[k],p+=(1<<k);
if(p<d[u]) res[id]+=(1<<k)*f[u][p].g[k];
if(x+1<d[u]) res[id]-=(1<<k)*f[u][x+1].g[k];
}
}
}
signed main()
{
ios::sync_with_stdio(false),cin.tie(0),cout.tie(0);
cin>>n,d[0]=-1;
for(int i=1;i<=n;i++) cin>>v[i];
for(int i=2,x;i<=n;i++) cin>>x,e[x].push_back(i);
cin>>q;
for(int i=1;i<=q;i++)
{
int x,k; cin>>x>>k;
a[x].push_back({k,i});
}
dfs(1),f[1]=cur,cur+=d[1],dfsb(1);
for(int i=1;i<=q;i++) cout<<res[i]<<'\n';
return 0;
}