【解题报告】[LNOI 2014] LCA/[GXOI/GZOI 2019] 旧词
【省选系列】[LNOI 2014] LCA/[GXOI/GZOI 2019] 旧词
首黑祭,好耶!
【LNOI 2014】LCA
首先考虑,每个节点对答案的贡献,我们可以发现LCA一定会在z到根节点的路径上,每个节点每次增加的贡献应该为\(deep[i]-(deep[i]-1)=1\),所以我们可以在区间[l,r]内的所有节点到根节点的路径上的点权增加1,不难发现每次查询的结果为z到根节点路径上的权值之和(本质上可以看做树上差分),考虑树剖+线段树
但是对于每次查询,我们都要重新建树并清空线段树,时间复杂度\(O(m*nlog^2n)\)显然会超时,我们可以考虑对区间进行差分,将每个询问区间[l,r]变为[1,l-1],[1,r],然后对每个区间按右端点进行排序,随后不断向询问中加入右端点,如果加入的点恰好是拆分出的区间的右端点我们就记录答案(1->z),最后的结果为ans_r-ans_l(此处的ans_l指的是l-1),可以将时间复杂度优化为\(O(nlog^2n)\)
AC code
点击查看代码
#include<iostream>
#include<cstdio>
#include<cstring>
#include<string>
#include<cmath>
#include<algorithm>
using namespace std;
const int maxn=50010;
const int mod=201314;
inline int read()
{
int w=0,f=1;
char ch=getchar();
while(ch<'0' || ch>'9')
{
if(ch=='-') f=-1;
ch=getchar();
}
while(ch>='0' && ch<='9')
{
w=(w<<3)+(w<<1)+(ch^48);
ch=getchar();
}
return w*f;
}
int fa[maxn],head[maxn];
int id[maxn],siz[maxn];
int top[maxn],deep[maxn];
int n,m,tot,max_son[maxn];
struct edge
{
int to,next;
}e[maxn*2];
struct s_t
{
int l,r;
int val;
int tag;
}t[maxn*4];
struct que
{
int ans_l,ans_r;
}q[maxn];
struct ques
{
int id,r,z;
bool check;
}qu[maxn*2];
void add(int x,int y)
{
e[++tot].to=y;
e[tot].next=head[x];
head[x]=tot;
}
void dfs_first(int x,int f)
{
siz[x]=1;fa[x]=f;
deep[x]=deep[f]+1;
for(int i=head[x];i;i=e[i].next)
{
int to=e[i].to;
if(to==f) continue;
dfs_first(to,x);
siz[x]+=siz[to];
if(siz[to]>siz[max_son[x]]) max_son[x]=to;
}
}
void dfs_second(int x,int t)
{
id[x]=++tot;top[x]=t;
if(max_son[x]==0) return ;
dfs_second(max_son[x],top[x]);
for(int i=head[x];i;i=e[i].next)
{
int to=e[i].to;
if(to!=fa[x] && to!=max_son[x]) dfs_second(to,to);
}
}
void build(int p,int l,int r)
{
t[p].l=l,t[p].r=r;
if(l==r) return ;
int mid=(l+r)>>1;
build(p*2,l,mid);
build(p*2+1,mid+1,r);
}
void push_down(int p)
{
if(t[p].tag)
{
t[p*2].val=(t[p*2].val+(t[p*2].r-t[p*2].l+1)*t[p].tag)%mod;
t[p*2].tag=(t[p*2].tag+t[p].tag)%mod;
t[p*2+1].val=(t[p*2+1].val+(t[p*2+1].r-t[p*2+1].l+1)*t[p].tag)%mod;
t[p*2+1].tag=(t[p*2+1].tag+t[p].tag)%mod;
t[p].tag=0;
}
}
void update(int p,int l,int r)
{
if(l<=t[p].l && t[p].r<=r)
{
t[p].val=(t[p].val+(t[p].r-t[p].l+1))%mod;
t[p].tag=(t[p].tag+1)%mod; return ;
}
push_down(p);
int mid=(t[p].r+t[p].l)>>1;
if(l<=mid) update(p*2,l,r);
if(r>mid) update(p*2+1,l,r);
t[p].val=(t[p*2].val+t[p*2+1].val)%mod;
}
int query(int p,int l,int r)
{
if(l<=t[p].l && t[p].r<=r) return t[p].val%mod;
push_down(p);
int mid=(t[p].l+t[p].r)>>1;
int ans=0;
if(l<=mid) ans=(ans+query(p*2,l,r))%mod;
if(r>mid) ans=(ans+query(p*2+1,l,r))%mod;
return ans;
}
void change(int x,int y)
{
while(top[x]!=top[y])
{
if(deep[top[x]]<deep[top[y]])
{
swap(x,y);
}
update(1,id[top[x]],id[x]);
x=fa[top[x]];
}
if(deep[x]>deep[y]) swap(x,y);
update(1,id[x],id[y]);
}
int get_sum(int x,int y)
{
int ans=0;
while(top[x]!=top[y])
{
if(deep[top[x]]<deep[top[y]])
{
swap(x,y);
}
ans=(ans+query(1,id[top[x]],id[x]))%mod;
x=fa[top[x]];
}
if(deep[x]>deep[y]) swap(x,y);
ans=(ans+query(1,id[x],id[y]))%mod;
return ans%mod;
}
bool cmp(ques x,ques y)
{
return x.r<y.r;
}
int main()
{
n=read();m=read();
for(int i=2;i<=n;i++)
{
int k=read()+1;
add(k,i);add(i,k);
}
tot=0,dfs_first(1,0);
tot=0,dfs_second(1,1);
build(1,1,n);
int cnt=0;
for(int i=1;i<=m;i++)
{
int l=read()+1;
int r=read()+1;
int z=read()+1;
qu[++cnt]={i,l-1,z,0};
qu[++cnt]={i,r,z,1};
}
sort(qu+1,qu+cnt+1,cmp);
int now=0;
for(int i=1;i<=cnt;i++)
{
while(now<qu[i].r)
{
change(1,++now);
}
int id=qu[i].id;
if(qu[i].check==0)
{
q[id].ans_l=get_sum(1,qu[i].z);
}
else
{
q[id].ans_r=get_sum(1,qu[i].z);
}
}
for(int i=1;i<=m;i++)
{
cout<<(q[i].ans_r-q[i].ans_l+mod)%mod<<'\n';
}
return 0;
}
【GXOI/GZOI 2019】旧词
相对于上一道题目而言,这道题目只是将维护的变为\(deep^k\),想到之前计算贡献的方法\(deep[i]^1-(deep[i]-1)^1=1\)
所以此处产生的贡献为\(deep[i]^k-(deep[i]-1)^k\),将幂预处理出来,建树时将对应的贡献预处理,然后线段树维护的操作变为了区间加贡献,区间查询,就可以切黑题力
AC code
点击查看代码
#include<iostream>
#include<cstdio>
#include<cstring>
#include<string>
#include<cmath>
#include<algorithm>
#define int long long
using namespace std;
const int maxn=50010;
const int mod=998244353;
inline int read()
{
int w=0,f=1;
char ch=getchar();
while(ch<'0' || ch>'9')
{
if(ch=='-') f=-1;
ch=getchar();
}
while(ch>='0' && ch<='9')
{
w=(w<<3)+(w<<1)+(ch^48);
ch=getchar();
}
return w*f;
}
int A[maxn],rev[maxn];
int fa[maxn],head[maxn];
int id[maxn],siz[maxn];
int top[maxn],deep[maxn];
int n,m,k,tot,max_son[maxn];
struct edge
{
int to,next;
}e[maxn*2];
struct s_t
{
int l,r;
int val;
int sum;
int tag;
}t[maxn*4];
struct que
{
int ans;
}q[maxn];
struct ques
{
int id,r,z;
}qu[maxn*2];
void add(int x,int y)
{
e[++tot].to=y;
e[tot].next=head[x];
head[x]=tot;
}
int qpow(int a,int b)
{
int ans=1;
for(;b;b>>=1,a=(a*a)%mod)
{
if(b&1) ans=(ans*a)%mod;
}
return ans%mod;
}
void dfs_first(int x,int f)
{
siz[x]=1;fa[x]=f;
deep[x]=deep[f]+1;
for(int i=head[x];i;i=e[i].next)
{
int to=e[i].to;
if(to==f) continue;
dfs_first(to,x);
siz[x]+=siz[to];
if(siz[to]>siz[max_son[x]]) max_son[x]=to;
}
}
void dfs_second(int x,int t)
{
id[x]=++tot;top[x]=t;rev[tot]=x;
if(max_son[x]==0) return ;
dfs_second(max_son[x],top[x]);
for(int i=head[x];i;i=e[i].next)
{
int to=e[i].to;
if(to!=fa[x] && to!=max_son[x]) dfs_second(to,to);
}
}
void build(int p,int l,int r)
{
t[p].l=l,t[p].r=r;
if(l==r)
{
t[p].val=(A[deep[rev[l]]]-A[deep[rev[l]]-1]+mod)%mod;
return ;
}
int mid=(l+r)>>1;
build(p*2,l,mid);
build(p*2+1,mid+1,r);
t[p].val=(t[p*2].val+t[p*2+1].val)%mod;
}
void push_down(int p)
{
if(t[p].tag)
{
t[p*2].sum=(t[p*2].sum+(t[p*2].val*t[p].tag)%mod)%mod;
t[p*2].tag=(t[p*2].tag+t[p].tag)%mod;
t[p*2+1].sum=(t[p*2+1].sum+(t[p*2+1].val*t[p].tag)%mod)%mod;
t[p*2+1].tag=(t[p*2+1].tag+t[p].tag)%mod;
t[p].tag=0;
}
}
void update(int p,int l,int r)
{
if(l<=t[p].l && t[p].r<=r)
{
t[p].sum=(t[p].sum+t[p].val)%mod;
t[p].tag++; return ;
}
push_down(p);
int mid=(t[p].r+t[p].l)>>1;
if(l<=mid) update(p*2,l,r);
if(r>mid) update(p*2+1,l,r);
t[p].sum=(t[p*2].sum+t[p*2+1].sum)%mod;
}
int query(int p,int l,int r)
{
if(l<=t[p].l && t[p].r<=r) return t[p].sum%mod;
push_down(p);
int mid=(t[p].l+t[p].r)>>1;
int ans=0;
if(l<=mid) ans=(ans+query(p*2,l,r))%mod;
if(r>mid) ans=(ans+query(p*2+1,l,r))%mod;
return ans%mod;
}
void change(int x,int y)
{
while(top[x]!=top[y])
{
if(deep[top[x]]<deep[top[y]])
{
swap(x,y);
}
update(1,id[top[x]],id[x]);
x=fa[top[x]];
}
if(deep[x]>deep[y]) swap(x,y);
update(1,id[x],id[y]);
}
int get_sum(int x,int y)
{
int ans=0;
while(top[x]!=top[y])
{
if(deep[top[x]]<deep[top[y]])
{
swap(x,y);
}
ans=(ans+query(1,id[top[x]],id[x]))%mod;
x=fa[top[x]];
}
if(deep[x]>deep[y]) swap(x,y);
ans=(ans+query(1,id[x],id[y]))%mod;
return ans%mod;
}
bool cmp(ques x,ques y)
{
return x.r<y.r;
}
signed main()
{
n=read();m=read();k=read();
for(int i=1;i<=n;i++)
{
A[i]=qpow(i,k)%mod;
}
for(int i=2;i<=n;i++)
{
int u=read();
add(u,i);add(i,u);
}
tot=0,dfs_first(1,0);
tot=0,dfs_second(1,1);
build(1,1,n);
int cnt=0;
for(int i=1;i<=m;i++)
{
int r=read();
int z=read();
qu[++cnt]={i,r,z};
}
sort(qu+1,qu+cnt+1,cmp);
int now=0;
for(int i=1;i<=cnt;i++)
{
while(now<qu[i].r)
{
change(1,++now);
}
int id=qu[i].id;
q[id].ans=get_sum(1,qu[i].z);
}
for(int i=1;i<=m;i++)
{
cout<<(q[i].ans)%mod<<'\n';
}
return 0;
}