Luogu P5305 [GXOI/GZOI2019]旧词

好套路的题目,和Luogu P4211 [LNOI2014]LCA基本上就是一个题

先考虑\(\sum{i\le x} \operatorname{depth(\operatorname{lca}(i,y))}\)怎么做(其实就是LNOI那题)

很容易发现此时我们两个点对的贡献为\(\operatorname{lca}\)的深度\(\Leftrightarrow\)两点与根节点公共路径长度

所以我们可以利用莫队+树剖,每次加一个点就是到根的一条链加\(1\),查询就是查某个点到根的路径和,复杂度是\(O(n\sqrt n\log^2 n)\)

然后发现删除其实完全没有必要,因此我们离线一下就变成\(O(n\log^2 n)\)的了

那么考虑这题怎么做,上面的算法相当于这里的\(k=1\),而这个累加的\(1\)其实就是\(( \operatorname{depth}(x)+1)^1-\operatorname{depth}(x)^1\),是一个树上差分的过程

因此这题每个点每次要加上的值就是\(( \operatorname{depth}(x)+1)^k-\operatorname{depth}(x)^k\),乍一看不好维护,其实仔细想一想每次加的都是定值

那么我们线段树处理出所有对应区间的每次增加值的和,修改的时候加上这个值即可。总体复杂度\(O(n\log^2 n)\)

CODE

#include<cstdio>
#include<cctype>
#include<vector>
#include<utility>
#define RI register int
#define CI const int&
#define Tp template <typename T>
using namespace std;
typedef pair <int,int> pi;
const int N=50005,mod=998244353;
struct edge
{
    int to,nxt;
}e[N]; int head[N],n,q,k,cnt,anc[N],dep[N],id[N],s[N],x,y,ans[N]; vector <pi> et[N];
class FileInputOutput
{
    private:
        static const int S=1<<21;
        #define tc() (A==B&&(B=(A=Fin)+fread(Fin,1,S,stdin),A==B)?EOF:*A++)
        #define pc(ch) (Ftop<S?Fout[Ftop++]=ch:(fwrite(Fout,1,S,stdout),Fout[(Ftop=0)++]=ch))
        char Fin[S],Fout[S],*A,*B; int Ftop,pt[15];
    public:
        Tp inline void read(T& x)
        {
            x=0; char ch; while (!isdigit(ch=tc()));
            while (x=(x<<3)+(x<<1)+(ch&15),isdigit(ch=tc()));
        }
        Tp inline void write(T x)
        {
            if (!x) return (void)(pc('0'),pc('\n')); RI ptop=0;
            while (x) pt[++ptop]=x%10,x/=10; while (ptop) pc(pt[ptop--]+48); pc('\n');
        }
        inline void Fend(void)
        {
            fwrite(Fout,1,Ftop,stdout);
        }
        #undef tc
        #undef pc
}F;
inline void inc(int& x,CI y)
{
    if ((x+=y)>=mod) x-=mod;
}
inline int sum(CI a,CI b)
{
    int t=a+b; return t>=mod?t-mod:t;
}
inline int sub(CI a,CI b)
{
    int t=a-b; return t<0?t+mod:t;
}
inline int quick_pow(int x,int p,int mul=1)
{
    for (;p;p>>=1,x=1LL*x*x%mod) if (p&1) mul=1LL*mul*x%mod; return mul;
}
inline void addedge(CI x,CI y)
{
    e[++cnt]=(edge){y,head[x]}; head[x]=cnt;
}
class Segment_Tree
{
    private:
        struct segment
        {
            int sum,val,tag;
        }node[N<<2];
        #define ls now<<1
        #define rs now<<1|1
        #define S(x) node[x].sum
        #define V(x) node[x].val
        #define T(x) node[x].tag
        inline void pushdown(CI now)
        {
            if (!T(now)) return; T(ls)+=T(now); inc(S(ls),1LL*T(now)*V(ls)%mod);
            T(rs)+=T(now); inc(S(rs),1LL*T(now)*V(rs)%mod); T(now)=0;
        }
    public:
        #define TN CI now=1,CI l=1,CI r=n
        #define LS ls,l,mid
        #define RS rs,mid+1,r
        inline void build(TN)
        {
            if (l==r) return (void)(V(now)=sub(quick_pow(dep[s[l]]+1,k),quick_pow(dep[s[l]],k)));
            int mid=l+r>>1; build(LS); build(RS); V(now)=sum(V(ls),V(rs));
        }
        inline int query(CI beg,CI end,TN)
        {
            if (beg<=l&&r<=end) return S(now); int mid=l+r>>1,ret=0; pushdown(now);
            if (beg<=mid) inc(ret,query(beg,end,LS)); if (end>mid) inc(ret,query(beg,end,RS)); return ret;
        }
        inline void modify(CI beg,CI end,TN)
        {
            if (beg<=l&&r<=end) return ++T(now),inc(S(now),V(now)); int mid=l+r>>1; pushdown(now);
            if (beg<=mid) modify(beg,end,LS); if (end>mid) modify(beg,end,RS); S(now)=sum(S(ls),S(rs));
        }
        #undef ls
        #undef rs
        #undef S
        #undef V
        #undef T
        #undef TN
        #undef LS
        #undef RS
}SEG;
class Heavy_Light_Division
{
    private:
        int son[N],top[N],size[N],idx;
    public:
        #define to e[i].to
        inline void DFS1(CI now)
        {
            size[now]=1; for (RI i=head[now];i;i=e[i].nxt)
            {
                dep[to]=dep[now]+1; DFS1(to); size[now]+=size[to];
                if (size[to]>size[son[now]]) son[now]=to;
            }
        }
        inline void DFS2(CI now,CI topf=1)
        {
            s[id[now]=++idx]=now; top[now]=topf; if (son[now]) DFS2(son[now],topf);
            for (RI i=head[now];i;i=e[i].nxt) if (to!=son[now]) DFS2(to,to);
        }
        inline int query(int nw,int ret=0)
        {
            while (top[nw]) inc(ret,SEG.query(id[top[nw]],id[nw])),nw=anc[top[nw]]; return ret;
        }
        inline void modify(int nw)
        {
            while (top[nw]) SEG.modify(id[top[nw]],id[nw]),nw=anc[top[nw]];
        }
        #undef to
}T;
int main()
{
    //freopen("CODE.in","r",stdin); freopen("CODE.out","w",stdout);
    RI i; for (F.read(n),F.read(q),F.read(k),i=2;i<=n;++i)
    F.read(anc[i]),addedge(anc[i],i); for (i=1;i<=q;++i)
    F.read(x),F.read(y),et[x].push_back(make_pair(y,i));
    for (T.DFS1(1),T.DFS2(1),SEG.build(),i=1;i<=n;++i)
    {
        T.modify(i); for (pi it:et[i]) ans[it.second]=T.query(it.first);
    }
    for (i=1;i<=q;++i) F.write(ans[i]); return F.Fend(),0;
}

转载于:https://www.cnblogs.com/cjjsb/p/10966974.html

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值