题意:
有n个结点,标为1~n,,形成了一棵以1为根节点的树,有m个操作,,
操作有两种:
1。某个节点的值加上v,该节点的隔代都加上v,,该节点的子代以及子代的隔代都减去v。
2。查询某个节点的值。
n,m<=2e5
如果修改节点值直接暴力dfs的话,时间复杂度为O(n*m),显然超时。
可以看出,修改操作,其实是修改两个区间的值,,,那么可以想到线段树区间修改,lazy一下时间就可以优化到m*logn;
关键是如何保证修改区间时,区间是连续。如果直接以节点的标号为区间下标,显然是不行的。
这时可以为这棵树维护一个dfs序(具体见代码),保证修改某个位置时,修改的区间是连续的。
代码:
#include <bits/stdc++.h>
using namespace std;
const int maxn = 200000+7;
struct node
{
int x1,x2,y1,y2;
}e[maxn];
int n,m,a[maxn],bef[maxn],lazy[maxn*4],index = 1;
vector<int> arr[maxn];
void dfs1(int u,int dep,int fa)
{
if(dep==1)
bef[u] = index++;
int len = arr[u].size();
for(int i = 0;i<len;i++)
{
int v = arr[u][i];
if(v!=fa)
{
dfs1(v,1-dep,u);
}
}
}
void dfs2(int u,int fa)
{
int len = arr[u].size();
for(int i = 0;i<len;i++)
if(arr[u][i]!=fa)
dfs2(arr[u][i],u);
int r1 = bef[u],l2 = maxn,r2 = 0;
for(int i = 0;i<len;i++)
{
int v = arr[u][i];
if(v!=fa)
{
r1 = max(e[v].y2,r1);
l2 = min(e[v].x1,l2);
r2 = max(e[v].y1,r2);
}
}
e[u].x1 = bef[u],e[u].y1 = r1;
e[u].x2 = l2,e[u].y2 = r2;
}
void pushdown(int rt)
{
lazy[rt<<1] += lazy[rt];
lazy[(rt<<1)+1] += lazy[rt];
lazy[rt] = 0;
}
void build(int rt,int l,int r)
{
if(l==r)
{
lazy[rt] = 0;
return ;
}
int mid = (l+r)>>1;
build(rt<<1,l,mid);
build((rt<<1)+1,mid+1,r);
lazy[rt] = 0;
}
void update(int rt,int l,int r,int ql,int qr,int v)
{
if(ql<=l&&r<=qr)
{
lazy[rt] += v;
return ;
}
pushdown(rt);
int mid = (l+r)>>1;
if(ql<=mid)
update(rt<<1,l,mid,ql,qr,v);
if(qr>mid)
update((rt<<1)+1,mid+1,r,ql,qr,v);
}
int query(int rt,int l,int r,int q)
{
if(l==r)
{
return lazy[rt];
}
pushdown(rt);
int mid = (l+r)>>1;
if(q<=mid)
return query(rt<<1,l,mid,q);
return query((rt<<1)+1,mid+1,r,q);
}
int main()
{
scanf("%d%d",&n,&m);
for(int i = 1;i<=n;i++)
scanf("%d",a+i);
for(int i = 1;i<=n-1;i++)
{
int u,v;
scanf("%d%d",&u,&v);
arr[u].push_back(v);
arr[v].push_back(u);
}
dfs1(1,1,0);
int len = arr[1].size();
for(int i = 0;i<len;i++)
dfs1(arr[1][i],1,1);
dfs2(1,0);
//for(int i = 1;i<=n;i++)
// cout<<e[i].x2<<" "<<e[i].y2<<endl;
build(1,1,n);
while(m--)
{
int ty;
scanf("%d",&ty);
if(ty==1)
{
int q,v;
scanf("%d%d",&q,&v);
//cout<<e[q].x1<<" "<<e[q].y1<<endl;
//cout<<e[q].x2<<" "<<e[q].y2<<endl;
update(1,1,n,e[q].x1,e[q].y1,v);
if(e[q].y2!=0)
update(1,1,n,e[q].x2,e[q].y2,-v);
}
else
{
int q;
scanf("%d",&q);
printf("%d\n",query(1,1,n,bef[q])+a[q]);
}
}
return 0;
}
区间修改+单点查询,用线段树写即耗内存,代码又长。
树状数组有一种操作可以高效的实现 区间修改+单点查询///。修改区间[L,R]相当于add(L,V);add(R+1,-V),,,但此时getSum(x)的值变为了下标为x的值。
代码:
#include <bits/stdc++.h>
using namespace std;
const int maxn = 200000+7;
struct node
{
int x1,x2,y1,y2;
}e[maxn];
int n,m,a[maxn],bef[maxn],tree[maxn],index = 1;
vector<int> arr[maxn];
void dfs1(int u,int dep,int fa)
{
if(dep==1)
bef[u] = index++;
int len = arr[u].size();
for(int i = 0;i<len;i++)
{
int v = arr[u][i];
if(v!=fa)
{
dfs1(v,1-dep,u);
}
}
}
void dfs2(int u,int fa)
{
int len = arr[u].size();
for(int i = 0;i<len;i++)
if(arr[u][i]!=fa)
dfs2(arr[u][i],u);
int r1 = bef[u],l2 = maxn,r2 = 0;
for(int i = 0;i<len;i++)
{
int v = arr[u][i];
if(v!=fa)
{
r1 = max(e[v].y2,r1);
l2 = min(e[v].x1,l2);
r2 = max(e[v].y1,r2);
}
}
e[u].x1 = bef[u],e[u].y1 = r1;
e[u].x2 = l2,e[u].y2 = r2;
}
int getSum(int x)
{
int ans = 0;
while(x>0)
{
ans += tree[x];
x -= x&(-x);
}
return ans;
}
void add(int k,int v)
{
while(k<=n)
{
tree[k] += v;
k += k&(-k);
}
}
int main()
{
scanf("%d%d",&n,&m);
memset(tree,0,sizeof(tree));
for(int i = 1;i<=n;i++)
scanf("%d",a+i);
for(int i = 1;i<=n-1;i++)
{
int u,v;
scanf("%d%d",&u,&v);
arr[u].push_back(v);
arr[v].push_back(u);
}
dfs1(1,1,0);
int len = arr[1].size();
for(int i = 0;i<len;i++)
dfs1(arr[1][i],1,1);
dfs2(1,0);
//for(int i = 1;i<=n;i++)
// cout<<e[i].x2<<" "<<e[i].y2<<endl;
while(m--)
{
int ty;
scanf("%d",&ty);
if(ty==1)
{
int q,v;
scanf("%d%d",&q,&v);
//cout<<e[q].x1<<" "<<e[q].y1<<endl;
//cout<<e[q].x2<<" "<<e[q].y2<<endl;
add(e[q].x1,v);
add(e[q].y1+1,-v);
if(e[q].y2!=0)
add(e[q].x2,-v),add(e[q].y2+1,v);
}
else
{
int q;
scanf("%d",&q);
printf("%d\n",getSum(bef[q])+a[q]);
}
}
return 0;
}