链接
http://www.lydsy.com/JudgeOnline/problem.php?id=4765
填坑
今日七道题。(4/7)
题解
套路大宗题目。
首先肯定先dfs序,记录in和out,这样就转成单点修改区间查询,显然树状数组。
那么如果求一个
sumi
的值,直接用树状数组减一下就出来了。它要你求
∑sumi
,似乎没啥特殊性质,那就直接分块吧。
一开始直接预处理出
sumi
的每一块的和,查询的时候整块的直接加进答案,散的可以用树状数组。
那么考虑修改对所维护信息的影响,树状数组上,直接单点修改就好了。
块上,显然一个块内有多少点是这个点的祖先(包括自己),答案就要加上几乘(v-last[v])。
那么,就要维护f[i][j]表示i这个块内,有多少点是j的祖先(包含j自身)。这个可以dfs求出,加一个桶,经过一个点就把那个点所在的快放进桶。对每个点扫描所有的桶,就能够求出f了。
查询的复杂度:
O(QN−−√logN)
修改的复杂度:
O(QN−−√)
其中
Q
是和
考虑优化,可以修改块的大小来平衡这个复杂度。
设块的大小为
x
修改:
查询:
O(Nx+xlogN)≈O(xlogN)
那就是要平衡
Nx+xlogN
均值不等式就发现
x=NlogN−−−−√
时时间复杂度最低。
这样复杂度是
O(NNlogN−−−−−−√)
好像没卵用
分析了这么半天,实际评测发现块大小改不改时间都一样哎╮(╯_╰)╭
代码
//分块+dfs序+树状数组
#include <cstdio>
#include <algorithm>
#include <cmath>
#define maxn 100010
#define maxs 320
#define lowbit(x) (x&-x)
#define ll unsigned long long
using namespace std;
ll ta[maxn], s[maxs], now[maxn], tid[maxn], out[maxn], tong[maxs], head[maxn], tot,
root, N, M, size, to[maxn*2], nex[maxn*2];
int f[maxs][maxn];
inline void adde(ll a, ll b){to[++tot]=b;nex[tot]=head[a];head[a]=tot;}
inline ll read(ll x=0)
{
char c=getchar();
while(c<48 or c>57)c=getchar();
while(c>=48 and c<=57)x=(x<<1)+(x<<3)+c-48,c=getchar();
return x;
}
void dfs(ll pos, ll pre)
{
ll p, i;
tid[pos]=++tid[0];
tong[pos/size]++;
for(i=1;i<=N/size;i++)f[i][pos]+=tong[i];
for(p=head[pos];p;p=nex[p])if(to[p]^pre)dfs(to[p],pos);
tong[pos/size]--;
out[pos]=tid[0];
}
inline void add(ll pos, ll v){for(;pos<=N;pos+=lowbit(pos))ta[pos]+=v;}
inline ll sum(ll pos)
{
ll ans=0;
for(;pos;pos-=lowbit(pos))ans+=ta[pos];
return ans;
}
inline ll subtree_sum(ll pos){return sum(out[pos])-sum(tid[pos]-1);}
void init()
{
ll i, a, b, l=log2(N);
N=read(), M=read();
size=l?sqrt(N):sqrt(N/l);
for(i=1;i<=N;i++)now[i]=read();
for(i=1;i<=N;i++)
{
a=read(),b=read();
if(!a)root=b;else adde(a,b),adde(b,a);
}
dfs(root,-1);
for(i=1;i<=N;i++)add(tid[i],now[i]);
for(i=1;i<=N;i++)s[i/size]+=subtree_sum(i);
}
inline void chg(ll u, ll v)
{
ll d=v-now[u], i;
add(tid[u],d);
for(i=1;i<=N/size;i++)s[i]+=f[i][u]*d;
now[u]=v;
}
inline void quiry(ll l, ll r)
{
ll i; ll ans=0;
for(i=l/size+1;i<r/size;i++)ans+=s[i];
for(i=l;i/size==l/size and i<=r;i++)ans+=subtree_sum(i);
if(l/size^r/size)for(i=r;i/size==r/size and i>=l;i--)ans+=subtree_sum(i);
printf("%llu\n",ans);
}
void work()
{
ll x, y, type;
while(M--)
{
type=read(), x=read(), y=read();
if(type==1)chg(x,y);
else quiry(x,y);
}
}
int main()
{
init();
work();
return 0;
}