【GDOI 2016 Day1】第四题 疯狂动物城

Description

这里写图片描述

抽象题意:求可持久化树链剖分

solution

一道巨型码农题!
对于每个数,我们要求 aji(i+1)/2 ,
拆一下: aj(i+q)(i+1+q)/2 ,瞬间可搞,
然后维护一大堆东西即可,

Code

#include<iostream>
#include<cstdlib>
#include<cstdio>
#include<cstring>
#include<cmath>
#define fo(i,a,b) for(int i=a;i<=b;i++)
#define efo(i,q) for(int i=A[q];i;i=B[i][0])
using namespace std;
typedef long long LL;
const int N=100500,maxlongint=2147483640;
const LL mo=20160501,ny=10080251;
int read(int &n)
{
    char ch=' ';int q=0,w=1;
    for(;(ch!='-')&&((ch<'0')||(ch>'9'));ch=getchar());
    if(ch=='-')w=-1,ch=getchar();
    for(;ch>='0' && ch<='9';ch=getchar())q=q*10+ch-48;n=q*w;return n;
}
int n,m,ANS;
int B[N*2][2],A[N],B0;
int root[N],now,NOW;
struct qqw{int c,s,fa,g,a,ld;int G[20];}a[N];
int zx[N],b0,ansl;
struct qqww
{int l,r,root;LL sl,sr,pl,pr,s,la;}b[N*78],ans;
void join(int q,int w)
{
    B[++B0][0]=A[q],A[q]=B0,B[B0][1]=w;
    B[++B0][0]=A[w],A[w]=B0,B[B0][1]=q;
}
int dfs1(int q,int e,int fa)
{
    a[q].fa=a[q].G[0]=fa;a[q].c=e;a[q].s=1;
    efo(i,q)if(B[i][1]!=fa)a[q].s+=dfs1(B[i][1],e+1,q);
    return a[q].s;
}
int dfs2(int q,int e,int fa)
{
    if(!e)e=q;
    a[q].g=++zx[0];zx[zx[0]]=q;a[q].ld=e;
    int mx=0;
    efo(i,q)if(B[i][1]!=fa && a[mx].s<a[B[i][1]].s)mx=B[i][1];
    if(mx)dfs2(mx,e,q);
    efo(i,q)if(B[i][1]!=fa && B[i][1]!=mx)dfs2(B[i][1],0,q);
}
void merge(qqww &ans,qqww a,qqww b,int a1,int b1)
{
    (ans.s=a.s+b.s)%=mo;
    (ans.sl=a.sl+b.sl+b.s*a1)%=mo;
    (ans.sr=a.sr+b1*a.s+b.sr)%=mo;
    (ans.pl=a.pl+b.pl+2*b.sl*a1+a1*a1*b.s)%=mo;
    (ans.pr=a.pr+2*a.sr*b1+b1*b1*a.s+b.pr)%=mo;
}
void build(int l,int r,int &e)
{
    e=++b0;
    if(l==r){b[e].pl=b[e].pr=b[e].sl=b[e].sr=0,b[e].s=a[zx[l]].a;return;}
    int t=(l+r)/2;
    build(l,t,b[e].l);build(t+1,r,b[e].r);
    merge(b[e],b[b[e].l],b[b[e].r],t-l+1,r-t);
}
int upa(int q,int e)
{
    while(a[q].c>e)
    {
        int i=0;
        while(a[a[q].G[i+1]].c>=e)i++;
        q=a[q].G[i];
    }
    return q;
}
int upb(int q,int w)
{
    while(q!=w)
    {
        int i=0;
        while(a[q].G[i+1]!=a[w].G[i+1])i++;
        q=a[q].G[i];w=a[w].G[i];
    }
    return q;
}
int fdds(int q,int w)
{
    int q1=upa(q,a[w].c),w1=upa(w,a[q].c);
    q1=upb(q1,w1);
    return a[q].c+a[w].c-2*a[q1].c+1;
}
void doit(qqww &q,int lo)
{
    if(!q.la)return;lo--;
    if(!lo)
    {
        q.s+=q.la;q.la=0;
        return;
    }
    b[++b0]=b[q.l],q.l=b0,(b[b0].la+=q.la)%=mo;
    b[++b0]=b[q.r],q.r=b0,(b[b0].la+=q.la)%=mo;
    LL w=lo*(lo+1)/2;
    if(w%3)(w*=(2*lo+1)/3)%=mo;else (w=w/3*(2*lo+1))%=mo;
    (w*=q.la)%=mo;
    (q.pl+=w)%=mo;(q.pr+=w)%=mo;
    w=(lo*(lo+1)/2)%mo*q.la%mo;
    (q.sl+=w)%=mo;(q.sr+=w)%=mo;
    (q.s+=(lo+1)*q.la)%=mo;
    q.la=0;
}
void find(int l,int r,int e,int l1,int r1)
{
    doit(b[e],r-l+1);
    if(l==l1 && r==r1)
    {
        qqww ans1=ans;
        merge(ans1,ans,b[e],l-ansl,r-l+1);
        ans=ans1;
        return;
    }
    int t=(l+r)/2;
    if(r1<=t)find(l,t,b[e].l,l1,r1),doit(b[b[e].r],r-t);
        else if(t<l1)doit(b[b[e].l],t-l+1),find(t+1,r,b[e].r,l1,r1);
            else find(l,t,b[e].l,l1,t),find(t+1,r,b[e].r,t+1,r1);
    merge(b[e],b[b[e].l],b[b[e].r],t-l+1,r-t);
}
LL ss(LL p,LL s,LL su,LL ad){return(p+2*s*ad+ad*ad*su+s+ad*su)%mo;}
LL solue(int q,int w)
{
    LL ANS=0,l=fdds(q,w)-1,r=0;
    int q1,w1;
    while((q1=a[q].ld)!=(w1=a[w].ld))
    {
        ans.s=ans.sl=ans.sr=ans.pl=ans.pr=0;
        if(a[q1].c>a[w1].c)
        {
            ansl=a[q1].g,find(1,n,root[now],a[q1].g,a[q].g);
            l-=a[q].c-a[q1].c+1;
            (ANS+=ss(ans.pl,ans.sl,ans.s,l+1))%=mo;
            q=a[q1].fa;
        }else
        {
            ansl=a[w1].g,find(1,n,root[now],a[w1].g,a[w].g);
            (ANS+=ss(ans.pr,ans.sr,ans.s,r))%=mo;
            r+=a[w].c-a[w1].c+1;
            w=a[w1].fa;
        }
    }
    ans.s=ans.sl=ans.sr=ans.pl=ans.pr=0;
    if(a[q].c>a[w].c)
        {
            ansl=a[w].g,find(1,n,root[now],a[w].g,a[q].g);
            l-=a[q].c-a[w].c+1;
            (ANS+=ss(ans.pl,ans.sl,ans.s,l+1))%=mo;
        }else
        {
            ansl=a[q].g,find(1,n,root[now],a[q].g,a[w].g);
            (ANS+=ss(ans.pr,ans.sr,ans.s,r))%=mo;
            r+=a[w].c-a[q].c+1;
        }
    return (ANS*ny)%mo;
}
void change(int l,int r,int e1,int &e2,int nw,int l1,int r1,int l2)
{
    doit(b[e1],r-l+1);
    if(b[e2].root!=nw)b[e2=++b0]=b[e1],b[e2].root=nw;else doit(b[e2],r-l+1);
    if(l==l1&&r==r1){b[e2].la+=l2;doit(b[e2],r-l+1);return;}
    int t=(l+r)/2;
    if(r1<=t)change(l,t,b[e1].l,b[e2].l,nw,l1,r1,l2),doit(b[b[e2].r],r-t);
        else if(t<l1)doit(b[b[e2].l],t-l+1),change(t+1,r,b[e1].r,b[e2].r,nw,l1,r1,l2);
            else change(l,t,b[e1].l,b[e2].l,nw,l1,t,l2),change(t+1,r,b[e1].r,b[e2].r,nw,t+1,r1,l2);
    merge(b[e2],b[b[e2].l],b[b[e2].r],t-l+1,r-t);
}
void modify(int q,int w,int e)
{
    int q1,w1;
    while((q1=a[q].ld)!=(w1=a[w].ld))
    {
        if(a[q1].c<a[w1].c)swap(q,w),swap(q1,w1);
        change(1,n,root[now],root[NOW+1],NOW+1,a[q1].g,a[q].g,e);
        q=a[q1].fa;
    }
    if(a[q].c<a[w].c)swap(q,w);
    change(1,n,root[now],root[NOW+1],NOW+1,a[w].g,a[q].g,e);
    now=++NOW;
} 
int main()
{
    freopen("zootopia.in","r",stdin);
    freopen("zootopia.out","w",stdout);
    int q,w,e,_;
    read(n),read(_);
    fo(i,1,n-1)read(q),read(w),join(q,w);
    fo(i,1,n)read(a[i].a);
    dfs1(1,1,0);
    fo(j,1,18)fo(i,1,n)a[i].G[j]=a[a[i].G[j-1]].G[j-1];
    dfs2(1,0,0);
    now=NOW=0;
    build(1,n,root[0]);
    LL last=0;
    while(_--)
    {
        read(e),read(q);q=q^last;
        if(e==1)read(w),read(e),w^=last,modify(q,w,e);
            else if(e==2)read(w),w^=last,last=solue(q,w),printf("%lld\n",last);
                else now=q;
    }
    return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值