bzoj3626[LNOI2014]LCA
传送门
思维题吧,神奇的模型转化,除了模型转化别的都是板子
最最最暴力的想法当然是一个一个去求lca啊,然后恭喜你获得了0分的好成绩
其实可以考虑另一种暴力,dep(lca(i,z))也可以看做是i点和z点到根节点路径上公共的路径的长度,那么我们就可以得到一个做法,将z到根的路径上每个节点都加上1的权值,然后对于[l,r]之间的点一一去求到根节点路径的权值和,这样依然没有分,但是这个思路很优秀。
我们发现这个做法是可逆的,所以我们可以将l~r间的点到根的路径上的点权值都加1,对于z求到根的权值和与之前的做法是等价的。
然后发现在线做很多路径都被重复加入和删除,所以考虑离线。
在统计答案时再用差分的思想将[l,r]处理成[1,r]-[1,l-1],就可以排序后保证每个点到根的路径只被插入一次了。
路径处理自然是树链剖分啦
代码:
#include<cstdio>
#include<iostream>
#include<algorithm>
using namespace std;
void read(int &x) {
char ch; bool ok;
for(ok=0,ch=getchar(); !isdigit(ch); ch=getchar()) if(ch=='-') ok=1;
for(x=0; isdigit(ch); x=x*10+ch-'0',ch=getchar()); if(ok) x=-x;
}
#define rg register
const int maxn=5e4+1,mod=201314;
int dep[maxn],top[maxn],tot,now,id[maxn],size[maxn],n,q,cnt,num,pre[maxn*2],nxt[maxn*2],h[maxn],f[maxn],ans[maxn],w;
struct oo{int x,y,id;}a[maxn*2];
struct segment_tree{int l,r,tag,sum;}s[maxn*4];
void add(int x,int y)
{
pre[++cnt]=y,nxt[cnt]=h[x],h[x]=cnt,
pre[++cnt]=x,nxt[cnt]=h[y],h[y]=cnt;
}
bool cmp(oo a,oo b){return a.x<b.x;}
void dfs(int x,int fa)
{
size[x]=1;
for(rg int i=h[x];i;i=nxt[i])
if(pre[i]!=fa)dep[pre[i]]=dep[x]+1,dfs(pre[i],x),size[x]+=size[pre[i]];
}
void dfs1(int x,int fa)
{
top[x]=fa,id[x]=++tot;int k=0;
for(rg int i=h[x];i;i=nxt[i])
if(dep[pre[i]]>dep[x]&&size[pre[i]]>size[k])k=pre[i];
if(!k)return ;dfs1(k,fa);
for(rg int i=h[x];i;i=nxt[i])
if(dep[pre[i]]>dep[x]&&pre[i]!=k)dfs1(pre[i],pre[i]);
}
void build(int x,int l,int r)
{
s[x].l=l,s[x].r=r;
if(l==r)return ;
int mid=(l+r)>>1;
build(x<<1,l,mid),build(x<<1|1,mid+1,r);
}
void update(int x){s[x].sum=(s[x<<1].sum+s[x<<1|1].sum)%mod;}
void pushdown(int x)
{
int ls=x<<1,rs=x<<1|1;
s[ls].tag+=s[x].tag,s[rs].tag+=s[x].tag;
(s[ls].sum+=s[x].tag*(s[ls].r-s[ls].l+1))%=mod,
(s[rs].sum+=s[x].tag*(s[rs].r-s[rs].l+1))%=mod;
s[x].tag=0;
}
void change(int x,int l,int r)
{
if(l<=s[x].l&&r>=s[x].r)
{
(s[x].sum+=(s[x].r-s[x].l+1))%=mod,s[x].tag++;
return ;
}
if(s[x].tag)pushdown(x);
int mid=(s[x].l+s[x].r)>>1;
if(l<=mid)change(x<<1,l,r);
if(r>mid)change(x<<1|1,l,r);
update(x);
}
void modify(int x,int y)
{
while(top[x]!=top[y])
{
if(dep[top[x]]<dep[top[y]])swap(x,y);
change(1,id[top[x]],id[x]);x=f[top[x]];
}
if(id[x]>id[y])swap(x,y);
change(1,id[x],id[y]);
}
void get(int x,int l,int r)
{
if(l<=s[x].l&&r>=s[x].r)
{
(w+=s[x].sum)%=mod;
return ;
}
if(s[x].tag)pushdown(x);
int mid=(s[x].l+s[x].r)>>1;
if(l<=mid)get(x<<1,l,r);
if(r>mid)get(x<<1|1,l,r);
update(x);
}
int qsum(int x,int y)
{
w=0;
while(top[x]!=top[y])
{
if(dep[top[x]]<dep[top[y]])swap(x,y);
get(1,id[top[x]],id[x]);x=f[top[x]];
}
if(id[x]>id[y])swap(x,y);
get(1,id[x],id[y]);return w;
}
int main()
{
read(n),read(q);
for(rg int i=2;i<=n;i++)read(f[i]),f[i]++,add(i,f[i]);
dfs(1,0),dfs1(1,1),build(1,1,tot);
for(rg int i=1,x,y,z;i<=q;i++)
{
read(x),read(y),read(z),x++,y++,z++;
a[++num].x=x-1,a[num].y=z,a[num].id=-i,a[++num].x=y,a[num].y=z,a[num].id=i;
}
sort(a+1,a+num+1,cmp);
for(rg int i=1;i<=num;i++)
{
while(now<a[i].x)modify(++now,1);
if(a[i].id<0)ans[-a[i].id]=qsum(a[i].y,1);
else ans[a[i].id]=(qsum(a[i].y,1)-ans[a[i].id]+mod)%mod;
}
for(rg int i=1;i<=q;i++)printf("%d\n",ans[i]);
}