P5305 [GXOI/GZOI2019] 旧词
观前提醒:这并不是本题的一般解法,但是思路过于抽象,而且卡常卡了好几天,故专门记录一下
注意到数据范围是 \(n,q \le 5e4\),考虑莫队(事实上本题的弱化版也是莫队)
\(x\) 是容易转移的,加上/减去变化的点与目前询问的点的 \(lca\) 产生的贡献即可, \(DFS\) 序求 \(lca\) 可以做到 \(O(1)\) 转移
考虑树上从一个点 \(u\) 到与之相邻的另一个点 \(v\) :(不妨设 \(dep_u<dep_v\))
转移前后,\(u\) 子树以外的点的 \(lca\) 并没有变化, \(u\) 子树内 \(v\) 子树外的点的 \(lca\) 也没有变化(还是 \(u\) ),只有 \(v\) 子树以内的点的 \(lca\) 会从 \(u\) 变成 \(v\) ,并改变相应的贡献
那就好说了,转移时查询 \(v\) 子树内编号 \(\le x\) 的点的数量就行了,树剖+主席树即可
\(dep_u>dep_v\)同理
跑一个块复杂度 \(O(2nlogn)\)
如果以 \(x\) 分块,块长为 \(len\),总复杂度 \(O(2n^2logn/len+nlen)\),实测 \(len\) 取 \(1700\) 时可过(最慢的点 \(705ms\))
代码:
#include<bits/stdc++.h>
#define rint register int
#define ll long long
using namespace std;
const int BUFSIZE = 1<<20;
char ibuf[BUFSIZE],*is=ibuf,*it=ibuf;
inline char getch()
{
if(is==it)
it=(is=ibuf)+fread(ibuf,1,BUFSIZE,stdin);
return is==it?EOF:*is++;
}
inline int read(){
int res=0,neg=0,ch=getch();
while(!(isdigit(ch) or ch == '-') and ch != EOF)
ch=getch();
if(ch=='-')
neg=1, ch=getch();
while(isdigit(ch))
res=res*10+(ch-'0'),ch=getch();
return neg?-res:res;
}
void write(int x)
{
if(x<0) x=-x,putchar('-');
if(x<10){putchar(x+'0');return;}
write(x/10ll);putchar(x%10ll+'0');
}
const int N=5e4+10,mod=998244353;
int n,q,k;
vector<int>ed[N];
ll qmi(ll a,int b)
{
ll res=1;
while(b)
{
if(b&1) res=(res*a)%mod;
a=(a*a)%mod;
b>>=1;
}
return res;
}
int len;
int in[N];
ll pre[N];
struct seg
{
int lson,rson;
int data;
}tr[N<<5];
int tot;
int root[N];
int build(int l,int r)
{
++tot;
if(l==r) return tot;
int mid=l+r>>1;
tr[tot].lson=build(l,mid),tr[tot].rson=build(mid+1,r);
return tot;
}
int upd(int lst,int l,int r,int k)
{
int p=++tot;
if(l==r)
{
tr[p].data++;
return p;
}
tr[p].data=tr[lst].data;
int mid=l+r>>1;
if(k<=mid) tr[p].lson=upd(tr[lst].lson,l,mid,k),tr[p].rson=tr[lst].rson;
else tr[p].lson=tr[lst].lson,tr[p].rson=upd(tr[lst].rson,mid+1,r,k);
tr[p].data=tr[tr[p].lson].data+tr[tr[p].rson].data;
return p;
}
int query(int p1,int p2,int l,int r,int k)
{
if(k>=r) return tr[p1].data-tr[p2].data;
int mid=l+r>>1,res=0;
res+=query(tr[p1].lson,tr[p2].lson,l,mid,k);
if(k>mid) res+=query(tr[p1].rson,tr[p2].rson,mid+1,r,k);
return res;
}
int dep[N],siz[N];
int fa[N];
int eul[N<<1],vis[N<<1],tote,dfn[N],num,rk[N];
int st[N<<1][21];
void dfs(int u,int fath)
{
siz[u]=1;
fa[u]=fath;
dep[u]=dep[fath]+1;
dfn[u]=++num;
rk[num]=u;
eul[++tote]=u,st[tote][0]=u;
if(!vis[u]) vis[u]=tote;
for(int v:ed[u])
{
dfs(v,u);
eul[++tote]=u,st[tote][0]=u;
siz[u]+=siz[v];
}
}
void init()
{
for(int j=1;j<19;j++)
for(int i=1;i+(1<<j-1)<=tote;i++)
{
int x=st[i][j-1],y=st[i+(1<<j-1)][j-1];
if(dep[x]<dep[y]) st[i][j]=x;
else st[i][j]=y;
}
}
int lca(int x,int y)
{
if(x>y) swap(x,y);
int j=__lg(y-x+1);
int l=st[x][j],r=st[y-(1<<j)+1][j];
if(dep[l]<dep[r]) return l;
else return r;
}
struct node
{
int x,y,id;
bool operator <(const node &a)const
{
if(in[x]==in[a.x])
{
if(in[x]&1) return dfn[y]<dfn[a.y];
else return dfn[y]>dfn[a.y];
}
return in[x]<in[a.x];
}
}ask[N];
ll res;
ll ans[N];
int x;
void modify(int u,int v,int op)
{
int sum=query(root[dfn[v]+siz[v]-1],root[dfn[v]-1],1,n,x);
res=((res+sum*(pre[dep[v]]-pre[dep[u]]+mod)*op)%mod+mod)%mod;
}
void modify(int u,int v)
{
int anc=lca(vis[u],vis[v]);
while(u!=anc)
{
modify(fa[u],u,-1);
u=fa[u];
}
while(u!=v)
{
modify(fa[v],v,1);
v=fa[v];
}
}
signed main()
{
n=read(),q=read(),k=read();
pre[0]=1;
for(rint i=1;i<=n;i++) pre[i]=qmi(i,k)%mod;
len=1700;
for(rint i=1;i<=n;i++) in[i]=(i/len)+1;
for(rint i=2;i<=n;i++)
{
int u=read();
ed[u].push_back(i);
}
dfs(1,0);
init();
root[0]=build(1,n);
for(rint i=1;i<=n;i++)
root[i]=upd(root[i-1],1,n,rk[i]);
for(rint i=1;i<=q;i++) ask[i].x=read(),ask[i].y=read(),ask[i].id=i;
sort(ask+1,ask+q+1);
x=0;
int u=1;
for(rint i=1;i<=q;i++)
{
while(x<ask[i].x) res=(res+pre[dep[lca(vis[++x],vis[u])]])%mod;
while(x>ask[i].x) res=(res-pre[dep[lca(vis[x--],vis[u])]]+mod)%mod;
while(dfn[u]<dfn[ask[i].y])
{
modify(u,rk[dfn[u]+1]);
u=rk[dfn[u]+1];
}
while(dfn[u]>dfn[ask[i].y])
{
modify(u,rk[dfn[u]-1]);
u=rk[dfn[u]-1];
}
ans[ask[i].id]=res;
}
for(rint i=1;i<=q;i++) write(ans[i]),puts("");
return 0;
}

浙公网安备 33010602011771号