您的位置:首页 > 其它

BZOJ 3730 震波

2018-01-26 09:44 337 查看

Description

在一片土地上有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

[b]先构造出一个动态点分树[/b]

[b]查询时,首先就考虑在当前块内和x距离为k的[/b]

[b]然后还要考虑其他含有x的树根,所以顺着点分树[/b]

[b]然后向上走到i,距离为d[/b]

[b]查询距离树根为k-d的点权和[/b]

[b]这些用线段树以距离为下标实现,每一个根动态开点[/b]

[b]但是还要减去原来子树的,因为不可能走重复的路径[/b]

[b]所以再维护一颗线段树,在往上跳时记录x传递到x上一个根的答案[/b]

[b]查询时减去x传递到上一根的答案就行了[/b]

[b]此题玄学卡常,可以换一种点分写法,这样可以省去get_size操作,快了不少[/b]

#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<cmath>
using namespace std;
#define MAX 120000
struct Node
{
int next,to;
}edge[MAX<<1];
int num,head[MAX];
int dep[MAX],fa[MAX],pos,size[MAX],minsize,root;
int FA[MAX],son[MAX],top[MAX],Size;
int sum[MAX*150];
int ch[MAX*150][2],RT[MAX<<1],n,m;
int ans,val[MAX];
bool vis[MAX];
inline int gi()
{
int x=0,flag=1;
char ch=getchar();
while (ch<'0'||ch>'9')
{
if (ch=='-') flag=-1;
ch=getchar();
}
while (ch>='0'&&ch<='9')
{
x=(x<<3)+(x<<1)+ch-'0';
ch=getchar();
}
return x*flag;
}
inline void add(int u,int v)
{
num++;
edge[num].next=head[u];
head[u]=num;
edge[num].to=v;
}
void dfs1(int x,int pa)
{
size[x]=1;
dep[x]=dep[pa]+1;
for (int i=head[x];i;i=edge[i].next)
{
int v=edge[i].to;
if (v==pa) continue;
fa[v]=x;
dfs1(v,x);
size[x]+=size[v];
if (size[son[x]]<size[v])
son[x]=v;
}
}
void dfs2(int x,int tp)
{
top[x]=tp;
if (son[x])
dfs2(son[x],tp);
for (int i=head[x];i;i=edge[i].next)
{
int v=edge[i].to;
if (v==fa[x]||v==son[x]) continue;
dfs2(v,v);
}
}
int lca(int x,int y)
{
while (top[x]!=top[y])
{
if (dep[top[x]]<dep[top[y]]) swap(x,y);
x=fa[top[x]];
}
if (dep[x]<dep[y]) return x;
return y;
}
int dis(int x,int y)
{
return dep[x]+dep[y]-(dep[lca(x,y)]<<1);
}
void get_root(int x,int pa)
{
size[x]=1;
int ret=0;
for (int i=head[x];i;i=edge[i].next)
{
int v=edge[i].to;
if (v==pa||vis[v]) continue;
get_root(v,x);
size[x]+=size[v];
if (size[v]>ret) ret=size[v];
}
if (Size-size[x]>ret) ret=Size-size[x];
if (ret<minsize) minsize=ret,root=x;
}
void solve(int x,int pa)
{
vis[x]=1;
FA[x]=pa;
for (int i=head[x];i;i=edge[i].next)
{
int v=edge[i].to;
if (vis[v]) continue;
minsize=n;Size=size[v];
get_root(v,x);
solve(root,x);
}
}
void update(int &rt,int l,int r,int x,int k)
{
if (!rt) rt=++pos;
sum[rt]+=k;
if (l==r)
return;
int mid=(l+r)>>1;
if (x<=mid) update(ch[rt][0],l,mid,x,k);
else update(ch[rt][1],mid+1,r,x,k);
}
int query(int rt,int l,int r,int L,int R)
{
if (!rt) return 0;
if (l>=L&&r<=R)
return sum[rt];
int mid=(l+r)>>1;
int s=0;
if (L<=mid) s+=query(ch[rt][0],l,mid,L,R);
if (R>mid) s+=query(ch[rt][1],mid+1,r,L,R);
return s;
}
void change(int u,int w)
{
update(RT[u],0,n,0,w);
for (int i=u;FA[i];i=FA[i])
{
int d=dis(u,FA[i]);
update(RT[FA[i]],0,n,d,w);
update(RT[i+n],0,n,d,w);
}
}
void ask(int u,int k)
{
ans+=query(RT[u],0,n,0,k);
for (int i=u;FA[i];i=FA[i])
{
int d=dis(u,FA[i]);
if (d>k) continue;
ans+=query(RT[FA[i]],0,n,0,k-d);
ans-=query(RT[i+n],0,n,0,k-d);
}
}
int main()
{int u,v;
cin>>n>>m;
for (int i=1;i<=n;i++)
val[i]=gi();
for (int i=1;i<=n-1;i++)
{
u=gi();v=gi();
add(u,v);add(v,u);
}
dfs1(1,0);
dfs2(1,1);
minsize=Size=n;
get_root(1,0);
solve(root,0);
for (int i=1;i<=n;i++)
change(i,val[i]);
ans=0;
for (int i=1;i<=m;i++)
{
int opt=gi();
int x=gi(),y=gi();
if (opt==0)
{
x^=ans;y^=ans;
ans=0;
ask(x,y);
printf("%d\n",ans);
}
else
{
x^=ans;y^=ans;
change(x,y-val[x]);
val[x]=y;
}
}
return 0;
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: