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]);
}

转载于:https://www.cnblogs.com/lcxer/p/10225216.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值