GDSOI 2018 Day3 基地

基地

Description

有一棵点数为 n n 的树,1号节点为根节点,对于剩下的任意节点 x x 他的父亲为x2,同时给出三个常数 a,b,c a , b , c x x 到它父亲的道路长度为ax2+bx+c,接下来有 m m 轮操作,每次操作会删除一颗子树,每个操作后,需要回答两个询问,1、找到一个点使得所有点到它的距离最短(如果有多个的话则为编号最小的那个) 2、输出最短的距离模998244353

Data Constrains

n1018 m104 a,b,c109 n ≤ 10 18   m ≤ 10 4   a , b , c ≤ 10 9

Solution

若没有第二个询问,那答案显然为整棵树的重心,求重心的话需要构造一个函数 ask_size(o) a s k _ s i z e ( o ) 表示以 o o 为根的子树大小,可以注意到如果n不在以 o o 为根的子树内,那以o为根的子树就是一棵完全二叉树,否则只有最底层需要特殊计算,同时动态开点记下子树大小已经被修改过的点的当前 size s i z e
现在考虑维护第二个询问,一样需要维护一个函数 ask_total(o) a s k _ t o t a l ( o ) 表示以 o o 为根的子树内的所有点经过以o为根的子树内的边到达根节点的路径总长,对于这个函数可以将整棵子树分成log层,对每一层的贡献分别求和即可。一样动态开点记下子树内贡献已经被修改过的点的 total t o t a l
时间复杂度 O(m log2 n) O ( m   l o g 2   n )

Code

#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<map>

#define fo(i,j,l) for(int i=j;i<=l;++i)
#define fd(i,j,l) for(int i=j;i>=l;--i)

using namespace std;
typedef long long ll;
const ll N=1e3,mo=998244353,n2=(mo+1)>>1,n6=(mo+1)/6;

map<ll,bool> done;
map<ll,ll> sz,size; 

ll d[N],n,m,xx,a,b,c;

inline ll cf2(ll o)
{
    o=o%mo;
    return o*(o+1)%mo*(2*o+1)%mo*n6%mo;
}

inline ll cf1(ll o)
{
    o=o%mo;
    return o*(o+1)%mo*n2%mo;
}

inline ll lj(ll le,ll ri)
{return (a*(cf2(ri)-cf2(le-1)+mo)+b*(cf1(ri)-cf1(le-1)+mo)+(ri-le+1)%mo*c)%mo;}

inline ll count(ll x)
{
    x=x%mo;
    return (a*x%mo*x%mo+b*x%mo+c)%mo;
}

inline ll len(ll x)
{
    int ok=1;
    fo(i,1,d[0])if(x==d[i]){
        ok=0; break;
    }
    ll v=x,ans=1;
    for(;v*2+1<=n;v=v*2+1)ans=ans*2+1;
    if(ok==1)return ans;
    if(v==n)return ans;
    v=v*2+1; ans=ans*2+1;
    return ans-v+n;
}

inline ll js(ll x)
{
    int ok=1;
    fo(i,1,d[0])if(x==d[i]){
        ok=0; break;
    }
    ll v=x,u=x,ans=0;
    for(;v*2<=n;)v=v<<1,u=(u<<1)^1;
    if(ok==1){
        ll cs=1;
        for(;v!=x;v>>=1,u>>=1,cs=(cs*2+1)%mo)
        ans=(ans+lj(v,u)*cs)%mo;
    }else{
        ll c1=3,c2,c3=1,k=n; ans=lj(v,n);
        if(k&1)c2=3;else c2=2;
        for(k>>=1,v>>=1,u>>=1;k!=x;k>>=1,v>>=1,u>>=1){
            if(k>v)ans=(ans+lj(v,k-1)*c1)%mo;
            ans=(ans+count(k)*c2)%mo;
            if(k<u)ans=(ans+lj(k+1,u)*c3)%mo;
            if(k&1)c2=(c2+c1+1)%mo;else c2=(c2+c3+1)%mo;
            c3=(c3*2+1)%mo; c1=(c1*2+1)%mo;
        }
    }
    return ans;
}

int main()
{
    cin>>n>>m>>a>>b>>c;
    ll op=n;
    for(;op;op>>=1)d[++d[0]]=op;
    ll zh=js(1);
    fo(i,1,m){
        scanf("%lld",&xx);
        if(done[xx]==false){
            ll kk=xx;int ok=1;
            for(;kk;kk>>=1)if(done[kk]==true){
                ok=0; break;
            }
            done[xx]=true;
            if(ok){
                ll del=sz[xx],kk=xx;
                del=(js(xx)-del+mo)%mo;
                ll tot=0,dx=(len(xx)-size[xx])%mo;
                for(;kk;kk>>=1){
                    sz[kk]=(sz[kk]+tot*dx+del)%mo;
                    tot=(tot+count(kk))%mo;
                }
                del=len(xx)-size[xx];   kk=xx;
                for(;kk;kk>>=1)size[kk]=size[kk]+del;
            }
        }
        ll dq=(zh-sz[1]+mo)%mo,x=1,lef=len(1)-size[1];
        for(;x*2<=n;){
            if(x*2==n){
                if(lef-2*size[n]<0&&!done[n])dq=(dq+(lef-2*(1-size[n]))%mo*count(n)%mo+mo)%mo,x=n;
                break;
            }else{
                ll ls=x<<1,rs=ls^1,d1=(lef-(len(ls)-size[ls])*2),d2=(lef-(len(rs)-size[rs])*2);
                if(d1<0&&!done[ls])dq=(dq+d1%mo*count(ls)%mo+mo)%mo,x=ls;
                else if(d2<0&&!done[rs])dq=(dq+d2%mo*count(rs)%mo+mo)%mo,x=rs;
                else break;
            }
        }
        printf("%lld %lld\n",x,dq);
    }
}
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值