震波
在一片土地上有N个城市,通过N-1条无向边互相连接,形成一棵树的结构,相邻两个城市的距离为1,其中第i个城市的价值为value[i]。
不幸的是,这片土地常常发生地震,并且随着时代的发展,城市的价值也往往会发生变动。
接下来你需要在线处理M次操作:
0 x k 表示发生了一次地震,震中城市为x,影响范围为k,所有与x距离不超过k的城市都将受到影响,该次地震造成的经济损失为所有受影响城市的价值和。
1 x y 表示第x个城市的价值变成了y。
为了体现程序的在线性,操作中的x、y、k都需要异或你程序上一次的输出来解密,如果之前没有输出,则默认上一次的输出为0。
Input
第一行包含两个正整数N和M。
第二行包含N个正整数,第i个数表示value[i]。
接下来N-1行,每行包含两个正整数u、v,表示u和v之间有一条无向边。
接下来M行,每行包含三个数,表示M次操作。
Output
包含若干行,对于每个询问输出一行一个正整数表示答案。
Sample Input
8 1 1 10 100 1000 10000 100000 1000000 10000000 1 2 1 3 2 4 2 5 3 6 3 7 3 8 0 3 1
Sample Output
11100101
Hint
1<=N,M<=100000
1<=u,v,x<=N
1<=value[i],y<=10000
0<=k<=N-1
题解
首先想到动态点分治+线段树
我们这时显然不能维护dfs序上的权值
我们可以对到当前点分中心的距离来开一个线段树
如果当前点分中心为u
那么线段树需要维护距离点u为 i 的所有点的权值之和
但是我们可能会算重,有可能点分中心的父亲会再一次计算某一些点的点权和
所有我们考虑如何去重
我们可以对点 u 再用一个线段树维护它的子树节点到它的点分树父亲的距离为 i 的点权和
在查询的时候减掉会被父亲计算的点的点权和即可
(强制在线,RE一般就是WA了,当然也有可能是真RE了)
代码:(查出来无数个sb错,还是一直RE,最后发现自己的动态开点线段树没有判等。。。)
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
inline int gi()
{
char c;int num=0;
while((c=getchar())<'0'||c>'9');
while(c>='0'&&c<='9'){num=num*10+c-48;c=getchar();}
return num;
}
#define N 100005
#define LOG 17
#define lc a[i].l,l,mid
#define rc a[i].r,mid+1,r
const int INF=0x3f3f3f3f;
int n,m;
int fir[N],to[2*N],nxt[2*N],cnt;
int val[N];
int tmpsiz[N],nrt,all;bool vis[N];
int dis[LOG][N],dfa[N],dep[N];
struct node{int l,r,x;}a[N*66];
int T1[N],T2[N],tot;
inline void adde(int a,int b)
{
to[++cnt]=b;nxt[cnt]=fir[a];fir[a]=cnt;
to[++cnt]=a;nxt[cnt]=fir[b];fir[b]=cnt;
}
void insert(int &i,int l,int r,int x,int k)
{
if(!i)i=++tot;
a[i].x+=k;
if(l==r)return;
int mid=(l+r)>>1;
if(x<=mid)insert(lc,x,k);
else insert(rc,x,k);
}
int query(int i,int l,int r,int qr)
{
if(!i)return 0ll;
if(l==r)return a[i].x;
int mid=(l+r)>>1;
if(qr<=mid)return query(lc,qr);
else return a[a[i].l].x+query(rc,qr);
}
void findrt(int u,int ff)
{
int mx=0;tmpsiz[u]=1;
for(int v,p=fir[u];p;p=nxt[p]){
if(!vis[v=to[p]]&&v!=ff){
findrt(v,u);
tmpsiz[u]+=tmpsiz[v];
mx=max(mx,tmpsiz[v]);
}
}
mx=max(mx,all-tmpsiz[u]);
if(2*mx<=all)nrt=u;
}
inline int getrt(int u,int sz)
{
nrt=-INF;all=sz;
findrt(u,0);return nrt;
}
void pre(int u,int ff,int d,int rt)
{
insert(T1[rt],0,n-1,dis[d][u],val[u]);
if(dfa[rt])insert(T2[rt],0,n-1,dis[d-1][u],val[u]);//
tmpsiz[u]=1;
for(int v,p=fir[u];p;p=nxt[p]){
if(!vis[v=to[p]]&&v!=ff){
dis[d][v]=dis[d][u]+1;
pre(v,u,d,rt);
tmpsiz[u]+=tmpsiz[v];
}
}
}
void DFZ(int u,int d)
{
vis[u]=1;dep[u]=d;
dis[d][u]=0;pre(u,0,d,u);
for(int v,p=fir[u];p;p=nxt[p]){
if(!vis[v=to[p]]){
dfa[v=getrt(v,tmpsiz[v])]=u;
DFZ(v,d+1);
}
}
}
inline void modify(int u,int k)
{
for(int t=u;t;t=dfa[t]){
insert(T1[t],0,n-1,dis[dep[t]][u],k-val[u]);//
if(dfa[t])insert(T2[t],0,n-1,dis[dep[t]-1][u],k-val[u]);//
}
val[u]=k;
}
inline int query(int u,int k)
{
long long ans=0;
for(int t=u;t;t=dfa[t]){
if(dfa[t]&&dis[dep[t]-1][u]<=k) ans-=query(T2[t],0,n-1,k-dis[dep[t]-1][u]);//
if(dis[dep[t]][u]<=k) ans+=query(T1[t],0,n-1,k-dis[dep[t]][u]);//
}
return int(ans);
}
int main()
{
int i,op,u,v;int ans=0;
n=gi();m=gi();
for(i=1;i<=n;i++)val[i]=gi();
for(i=1;i<n;i++){u=gi();v=gi();adde(u,v);}
DFZ(getrt(1,n),0);
for(i=1;i<=m;i++){
op=gi();u=gi();v=gi();
u^=ans;v^=ans;
if(op)modify(u,v);
else printf("%d\n",ans=query(u,v));
}
}
烁烁的游戏
背景:烁烁很喜欢爬树,这吓坏了树上的皮皮鼠。
题意:
给定一颗n个节点的树,边权均为1,初始树上没有皮皮鼠。
烁烁他每次会跳到一个节点u,把周围与他距离不超过d的节点各吸引出w只皮皮鼠。皮皮鼠会被烁烁吸引,所以会一直待在节点上不动。
烁烁很好奇,在当前时刻,节点u有多少个他的好朋友---皮皮鼠。
大意:
给一颗n个节点的树,边权均为1,初始点权均为0,m次操作:
Q x:询问x的点权。
M x d w:将树上与节点x距离不超过d的节点的点权均加上w。
Input
第一行两个正整数:n,m
接下来的n-1行,每行三个正整数u,v,代表u,v之间有一条边。
接下来的m行,每行给出上述两种操作中的一种。
Output
对于每个Q操作,输出当前x节点的皮皮鼠数量。
Sample Input
7 6 1 2 1 4 1 5 2 3 2 7 5 6 M 1 1 2 Q 5 M 2 2 3 Q 3 M 1 2 1 Q 2
Sample Output
2 3 6
Hint
数据范围:
n,m<=10^5,|w|<=10^4
注意:w不一定为正整数,因为烁烁可能把皮皮鼠吓傻了。
题解
这题就是上一道题的逆向版
上一道题:单点修改、前缀查询(好像可以用动态开点树状数组)
这一道题:前缀修改、单点查询
直接差分一下,变成单点修改、后缀查询(其实也可以单点修改两次,前缀查询,但是常数会大一些吧。。。。)
然后直接上动态点分治了,基本和上一题一样
代码:
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
inline int gi()
{
char c;int num=0,flg=1;
while((c=getchar())<'0'||c>'9')if(c=='-')flg=-1;
while(c>='0'&&c<='9'){num=num*10+c-48;c=getchar();}
return num*flg;
}
#define N 100005
#define LOG 17
const int INF=0x3f3f3f3f;
int n,m;
int fir[N],to[2*N],nxt[2*N],cnt;
void adde(int a,int b)
{
to[++cnt]=b;nxt[cnt]=fir[a];fir[a]=cnt;
to[++cnt]=a;nxt[cnt]=fir[b];fir[b]=cnt;
}
int tmpsiz[N],nrt,all;bool vis[N];
int dis[LOG][N],dfa[N],dep[N];
void findrt(int u,int ff)
{
int mx=0;tmpsiz[u]=1;
for(int v,p=fir[u];p;p=nxt[p]){
if(!vis[v=to[p]]&&v!=ff){
findrt(v,u);
tmpsiz[u]+=tmpsiz[v];
mx=max(mx,tmpsiz[v]);
}
}
mx=max(mx,all-tmpsiz[u]);
if(mx+mx<=all)nrt=u;
}
int getrt(int u,int sz)
{
nrt=-INF;all=sz;
findrt(u,0);return nrt;
}
#define lc a[i].l
#define rc a[i].r
struct node{int l,r,x;}a[N<<7];
int T1[N],T2[N],tot;
void insert(int &i,int l,int r,int x,int k)
{
if(!i)i=++tot;a[i].x+=k;if(l==r)return;
int mid=(l+r)>>1;
if(x<=mid)insert(lc,l,mid,x,k);
else insert(rc,mid+1,r,x,k);
}
int query(int i,int l,int r,int ql)
{
if(!i)return 0;
if(l==r)return a[i].x;
int mid=(l+r)>>1;
if(ql<=mid)return query(lc,l,mid,ql)+a[rc].x;
else return query(rc,mid+1,r,ql);
}
void pre(int u,int ff,int d)
{
tmpsiz[u]=1;
for(int v,p=fir[u];p;p=nxt[p]){
if(!vis[v=to[p]]&&v!=ff){
dis[d][v]=dis[d][u]+1;
pre(v,u,d);
tmpsiz[u]+=tmpsiz[v];
}
}
}
void DFZ(int u,int d)
{
vis[u]=1;dep[u]=d;
pre(u,0,d);
for(int v,p=fir[u];p;p=nxt[p]){
if(!vis[v=to[p]]){
dfa[v=getrt(v,tmpsiz[v])]=u;
DFZ(v,d+1);
}
}
}
inline void modify(int u,int k,int s)
{
for(int t=u;t;t=dfa[t]){
if(dis[dep[t]][u]<=k) insert(T1[t],0,n,k-dis[dep[t]][u],s);//
if(dfa[t]&&dis[dep[t]-1][u]<=k) insert(T2[t],0,n,k-dis[dep[t]-1][u],s);//
}
}
inline int query(int u)
{
long long ans=0;
for(int t=u;t;t=dfa[t]){
ans+=query(T1[t],0,n,dis[dep[t]][u]);//
if(dfa[t]) ans-=query(T2[t],0,n,dis[dep[t]-1][u]);//
}
return int(ans);
}
char op[3];
int main()
{
int i,u,v,s;
n=gi();m=gi();
for(i=1;i<n;i++){u=gi();v=gi();adde(u,v);}
DFZ(getrt(1,n),0);
for(i=1;i<=m;i++){
scanf("%s",op);
if(op[0]=='M'){
u=gi();v=gi();s=gi();
modify(u,v,s);
}
else printf("%d\n",query(gi()));
}
}