题解 CF757G 【Can Bash Save the Day?】
现有的题解里一篇是动态点分治的题解,另一篇介绍了可持久化线段树的做法但不甚详尽。本文将详细分析后一种做法。
首先我们观察一下询问的特点。
不妨钦定
那么分析题中询问式,很容易想到拆成两个前缀的差值,换言之,只要对于给定的
而
在修改的时候,式子的第一部分值
关键问题是:如何转换
这个模型明显具有可加性,也就是说,为了计算
问题又转变成了:如何快速在树上执行路径加、路径求和操作?又如何对于每个节点
前一个问题的答案是树链剖分,后一个问题的答案是可持久化线段树。
由于边在程序中不好表示,我们把边上的问题转移到点上:对于每条边,我们转移到该边的下方节点上维护。也就是说,对于每个节点
梳理一下,对于每个新点的加入,操作步骤如下:
-
找到在树链剖分意义下,该点到根的路径被划分成的区间;
-
所有区间对
cnt 执行区间加一操作。
但普通的可持久化线段树不支持区间加操作,对此有下列两种方法:
方法一:差分
在线段树上维护所有
而此时
这个方法效率不佳,空间复杂度较高,目前不能通过本题的数据。各位大佬可以帮忙调一下下面的代码:
#include<cstdio>
#include<cstring>
#include<vector>
#include<algorithm>
using namespace std;
int n=0,q=0;
int p[250000];
struct e
{
int to,val;e(int t=0,int v=0):to(t),val(v){}
};
vector<e> ed[250000];
int fa[250000],sz[250000],son[250000];long long dep[250000],val[250000];
void dfs_bas(int u,int f)
{
fa[u]=f,sz[u]=1,son[u]=0;
for(int i=0;i<ed[u].size();i++)
{
int v=ed[u][i].to;
if(v!=f)
{
dep[v]=dep[u]+ed[u][i].val,val[v]=ed[u][i].val;
dfs_bas(v,u);
sz[u]+=sz[v];
if(sz[v]>sz[son[u]])son[u]=v;
}
}
}
int seg[250000],rev[250000],top[250000];
void dfs_sec(int pos=1,int f=0)
{
if(son[pos])
{
seg[0]++;seg[son[pos]]=seg[0];
top[son[pos]]=top[pos];
rev[seg[0]]=son[pos];
dfs_sec(son[pos],pos);
}
for(int i=0,v=0;i<ed[pos].size();i++)
{
v=ed[pos][i].to;
if(v!=son[pos]&&v!=f)
{
seg[0]++;seg[v]=seg[0];
rev[seg[0]]=v;
top[v]=v;
dfs_sec(v,pos);
}
}
}
long long s[250000];
struct nd
{
int lch,rch;int sum;long long sum_s;
};
nd t[32000001];
int used=0;
void build(int l,int r,int &k)
{
if(!k)k=++used;
t[k].lch=t[k].rch=0;
if(l==r)
{
t[k].sum=t[k].sum_s=0;return;
}
int mid=(l+r)>>1;
build(l,mid,t[k].lch),build(mid+1,r,t[k].rch);
t[k].sum=t[t[k].lch].sum+t[t[k].rch].sum,t[k].sum_s=t[t[k].lch].sum_s+t[t[k].rch].sum;
}
int change(int now,int l,int r,int pos,int val,int old)
{
if(pos<1||pos>n)return now;
int p;
if(now==old)
{
p=++used;t[p]=t[now];
}
else
{
p=now;
}
if(l==r)
{
t[p].sum+=val,t[p].sum_s+=s[pos-1]*val;return p;
}
int mid=(l+r)>>1;
if(pos<=mid)
{
t[p].lch=change(t[now].lch,l,mid,pos,val,t[old].lch);
t[p].sum=t[t[p].lch].sum+t[t[p].rch].sum,t[p].sum_s=t[t[p].lch].sum_s+t[t[p].rch].sum_s;
}
else
{
t[p].rch=change(t[now].rch,mid+1,r,pos,val,t[old].rch);
t[p].sum=t[t[p].lch].sum+t[t[p].rch].sum,t[p].sum_s=t[t[p].lch].sum_s+t[t[p].rch].sum_s;
}
return p;
}
long long query_sum(int now,int l,int r,int x,int y)
{
if(l>y||r<x)return 0;
if(x<=l&&r<=y)return t[now].sum;
int mid=(l+r)/2;
return query_sum(t[now].lch,l,mid,x,y)+query_sum(t[now].rch,mid+1,r,x,y);
}
long long query_sum_s(int now,int l,int r,int x,int y)
{
if(l>y||r<x)return 0;
if(x<=l&&r<=y)return t[now].sum_s;
int mid=(l+r)/2;
return query_sum_s(t[now].lch,l,mid,x,y)+query_sum_s(t[now].rch,mid+1,r,x,y);
}
int rt[250000];
int add_road(int tree_id,int pos,int val)
{
int fpos=0;int k=rt[tree_id];
while(pos)
{
fpos=top[pos];
k=change(k,1,n,seg[fpos],val,rt[tree_id]);
k=change(k,1,n,seg[pos]+1,-val,rt[tree_id]);
pos=fa[fpos];
}
return k;
}
long long query_single(int tree_id,int k)
{
long long sum=query_sum(rt[tree_id],1,n,1,k),sum_s=query_sum_s(rt[tree_id],1,n,1,k);
return sum*s[k]-sum_s;
}
long long query_point(int tree_id,int pos)
{
int fpos=0;long long sum=0;
while(pos)
{
fpos=top[pos];
sum+=query_single(tree_id,seg[pos])-query_single(tree_id,seg[fpos]-1);
pos=fa[fpos];
}
return sum;
}
long long sum_dep[250000];
long long sum_dist(int tree_id,int v)
{
return sum_dep[tree_id]+dep[v]*tree_id-2*query_point(tree_id,v);
}
int main()
{
scanf("%d%d",&n,&q);for(int i=1;i<=n;i++)scanf("%d",&p[i]);
for(int i=1,x=0,y=0,w=0;i<n;i++){scanf("%d%d%d",&x,&y,&w);ed[x].push_back(e(y,w)),ed[y].push_back(e(x,w));}
dfs_bas(1,0);
seg[0]=seg[1]=top[1]=rev[1]=1;dfs_sec(1);
for(int i=1;i<=n;i++)s[i]=s[i-1]+val[rev[i]];
build(1,n,rt[0]);
for(int i=1;i<=n;i++)rt[i]=add_road(i-1,p[i],1);
for(int i=1;i<=n;i++)sum_dep[i]=sum_dep[i-1]+dep[p[i]];
int type=0,l=0,r=0,v=0,x=0;
long long last_ans=0;
int cnt=0;
while(q--)
{
scanf("%d",&type);
if(type==1)
{
scanf("%d%d%d",&l,&r,&v);
l=last_ans^l,r=last_ans^r,v=last_ans^v;
last_ans=sum_dist(r,v)-sum_dist(l-1,v);
printf("%lld\n",last_ans);
last_ans%=1<<30;
}
else
{
cnt++;
scanf("%d",&x);
x=last_ans^x;
sum_dep[x]=sum_dep[x-1]+dep[p[x+1]];
if(used>3e7)
{
swap(p[x],p[x+1]);
used=0;
memset(rt,0,sizeof(rt));build(1,n,rt[0]);
for(int i=1;i<=n;i++)rt[i]=add_road(i-1,p[i],1);
cnt=0;
}
else
{
rt[x]=add_road(x-1,p[x+1],1);
swap(p[x],p[x+1]);
}
}
}
}
方法二:标记永久化
其实可持久化线段树区间修改问题更常用的方法是标记永久化。不会的可以自行搜索这题的题解。代码:
#include<cstdio>
#include<cstring>
#include<vector>
#include<algorithm>
using namespace std;
int n=0,q=0;
int p[250000];
struct e
{
int to,val;e(int t=0,int v=0):to(t),val(v){}
};
vector<e> ed[250000];
int fa[250000],sz[250000],son[250000];long long dep[250000],val[250000];
void dfs_bas(int u,int f)
{
fa[u]=f,sz[u]=1,son[u]=0;
for(int i=0;i<ed[u].size();i++)
{
int v=ed[u][i].to;
if(v!=f)
{
dep[v]=dep[u]+ed[u][i].val,val[v]=ed[u][i].val;
dfs_bas(v,u);
sz[u]+=sz[v];
if(sz[v]>sz[son[u]])son[u]=v;
}
}
}
int seg[250000],rev[250000],top[250000];
void dfs_sec(int pos=1,int f=0)
{
if(son[pos])
{
seg[0]++;seg[son[pos]]=seg[0];
top[son[pos]]=top[pos];
rev[seg[0]]=son[pos];
dfs_sec(son[pos],pos);
}
for(int i=0,v=0;i<ed[pos].size();i++)
{
v=ed[pos][i].to;
if(v!=son[pos]&&v!=f)
{
seg[0]++;seg[v]=seg[0];
rev[seg[0]]=v;
top[v]=v;
dfs_sec(v,pos);
}
}
}
long long s[250000];
struct nd
{
int lch,rch;int sum;long long sum_s;
};
nd t[30000001];
int used=0,lim=0;
void change(int &now,int l,int r,int x,int y)
{
if(now<=lim)
{
++used;t[used]=t[now];now=used;
}
int mid=(l+r)>>1;
if(x<=l&&r<=y)
{
t[now].sum++;t[now].sum_s+=s[r]-s[l-1];return;
}
if(x<=mid)change(t[now].lch,l,mid,x,y);
if(y>mid)change(t[now].rch,mid+1,r,x,y);
t[now].sum_s=t[t[now].lch].sum_s+t[t[now].rch].sum_s+(s[r]-s[l-1])*t[now].sum;
}
long long query(int now,int l,int r,int x,int y,int cnt)
{
if(now==0)return cnt*(s[min(r,y)]-s[max(l,x)-1]);
if(x<=l&&r<=y)return t[now].sum_s+cnt*(s[r]-s[l-1]);
int mid=(l+r)>>1;
long long ans=0;
if(x<=mid)ans+=query(t[now].lch,l,mid,x,y,cnt+t[now].sum);
if(y>mid)ans+=query(t[now].rch,mid+1,r,x,y,cnt+t[now].sum);
return ans;
}
int rt[250000];
void add_road(int tree_id,int pos)
{
lim=used;
int fpos=0;
while(pos)
{
fpos=top[pos];
change(rt[tree_id],1,n,seg[fpos],seg[pos]);
pos=fa[fpos];
}
}
long long sum_dep[250000];
long long sum_dist(int tree_id,int v)
{
int pos=v,fpos=0;long long sum=0;
while(pos)
{
fpos=top[pos];
sum+=query(rt[tree_id],1,n,seg[fpos],seg[pos],0);
pos=fa[fpos];
}
return sum_dep[tree_id]+dep[v]*tree_id-2*sum;
}
int main()
{
scanf("%d%d",&n,&q);for(int i=1;i<=n;i++)scanf("%d",&p[i]);
for(int i=1,x=0,y=0,w=0;i<n;i++){scanf("%d%d%d",&x,&y,&w);ed[x].push_back(e(y,w)),ed[y].push_back(e(x,w));}
dfs_bas(1,0);
seg[0]=seg[1]=top[1]=rev[1]=1;dfs_sec(1);
for(int i=1;i<=n;i++)s[i]=s[i-1]+val[rev[i]];
for(int i=1;i<=n;i++)rt[i]=rt[i-1],add_road(i,p[i]);
for(int i=1;i<=n;i++)sum_dep[i]=sum_dep[i-1]+dep[p[i]];
int type=0,l=0,r=0,v=0,x=0;
long long last_ans=0;
int cnt=0;
while(q--)
{
scanf("%d",&type);
if(type==1)
{
scanf("%d%d%d",&l,&r,&v);
l=last_ans^l,r=last_ans^r,v=last_ans^v;
last_ans=sum_dist(r,v)-sum_dist(l-1,v);
printf("%lld\n",last_ans);
last_ans%=1<<30;
}
else
{
cnt++;
scanf("%d",&x);
x=last_ans^x;
sum_dep[x]=sum_dep[x-1]+dep[p[x+1]];
if(used>2.99e7)
{
swap(p[x],p[x+1]);
used=0;
for(int i=1;i<=n;i++)rt[i]=rt[i-1],add_road(i,p[i]);
cnt=0;
}
else
{
rt[x]=rt[x-1];add_road(x,p[x+1]);
swap(p[x],p[x+1]);
}
}
}
}