[NOI2020] 命运
\(\text{Problem}:\)[NOI2020] 命运
\(\text{Solution}:\)
关键性质:若满足限制 \((u,v_{1})\),有 \(dep_{v2}<dep_{v1}\) 且 \(v_{2}\) 是 \(v_{1}\) 的祖先,那么满足限制 \((u,v_{2})\)。
设 \(f_{x,i}\) 表示已经确定 \(x\) 子树内边的状态,下端点在 \(x\) 的子树内且不满足条件的限制中,上端点的深度最大值为 \(i\) 的方案数。特殊的 \(f_{x,0}\) 表示所有限制都满足条件。考虑逐个合并其儿子结点 \(y\) 的答案,以 \(x\rightarrow y\) 这条边为 \(0/1\) 进行分类讨论:
- \(x\rightarrow y\) 为 \(1\) 边,则对于所有 \(f_{y,j}\),只需满足 \(j<dep_{y}\) 都可以转移给 \(f_{x,i}\)。
- \(x\rightarrow y\) 为 \(0\) 边,则对于所有 \(f_{x,j},f_{y,k}\),满足 \(j=i\) 或 \(k=i\),且 \(j,k\leq i\) 即可转移给 \(f_{x,i}\)。
故得到转移为:
\[f_{x,i}\sum\limits_{j=0}^{dep_{x}}f_{y,j}+f_{x,i}\sum\limits_{j=0}^{i}f_{y,j}+f_{y,i}\sum\limits_{j=0}^{i-1}f_{x,j}\rightarrow f_{x,i}
\]
不难发现,转移有非常明显的前缀和结构。设 \(g_{x,i}=\sum\limits_{j=0}^{i}f_{x,j}\),有:
\[f_{x,i}\times(g_{y,dep_{x}}+g_{y,i})+f_{y,i}\times g_{x,i-1}\rightarrow f_{x,i}
\]
其中 \(g_{y,dep_{x}}\) 可以直接查询得到,剩下的部分类似 [PKUWC2018] Minimax,进行线段树合并,在线段树上维护区间和优化 \(dp\) 即可。
\(\text{Code}:\)
#include <bits/stdc++.h>
#pragma GCC optimize(3)
//#define int long long
#define ri register
#define mk make_pair
#define fi first
#define se second
#define pb push_back
#define eb emplace_back
#define is insert
#define es erase
#define vi vector<int>
#define vpi vector<pair<int,int>>
using namespace std; const int N=500010, Mod=998244353;
inline int read()
{
int s=0, w=1; ri char ch=getchar();
while(ch<'0'||ch>'9') { if(ch=='-') w=-1; ch=getchar(); }
while(ch>='0'&&ch<='9') s=(s<<3)+(s<<1)+(ch^48), ch=getchar();
return s*w;
}
int n,m,mxd,d[N]; vector<int> q[N];
int head[N],maxE; struct Edge { int nxt,to; }e[N<<1];
inline void Add(int u,int v) { e[++maxE].nxt=head[u]; head[u]=maxE; e[maxE].to=v; }
void DFS1(int x,int fa)
{
d[x]=d[fa]+1, mxd=max(mxd,d[x]);
for(ri int i=head[x];i;i=e[i].nxt)
{
int v=e[i].to;
if(v==fa) continue;
DFS1(v,x);
}
}
struct Tree { int lc,rc,sum,mul; }D[N*30]; int root[N],cnt;
inline void ps(int x) { D[x].sum=(D[D[x].lc].sum+D[D[x].rc].sum)%Mod; }
inline void Change(int x,int k) { D[x].sum=1ll*D[x].sum*k%Mod, D[x].mul=1ll*D[x].mul*k%Mod; }
inline void pd(int x)
{
if(!x||D[x].mul==1) return;
if(D[x].lc) Change(D[x].lc,D[x].mul);
if(D[x].rc) Change(D[x].rc,D[x].mul);
D[x].mul=1;
}
void UpDate(int &x,int l,int r,int pos)
{
x=++cnt;
D[x].sum=D[x].mul=1;
if(l==r) return;
int mid=(l+r)/2;
if(pos<=mid) UpDate(D[x].lc,l,mid,pos);
else UpDate(D[x].rc,mid+1,r,pos);
ps(x);
}
int Ask(int x,int u,int v,int l,int r)
{
if(!x) return 0;
if(l>=u&&r<=v) return D[x].sum;
pd(x);
int mid=(l+r)/2;
if(v<=mid) return Ask(D[x].lc,u,v,l,mid);
if(u>mid) return Ask(D[x].rc,u,v,mid+1,r);
return (Ask(D[x].lc,u,mid,l,mid)+Ask(D[x].rc,mid+1,v,mid+1,r))%Mod;
}
int Merge(int x,int y,int l,int r,int gx,int gy)
{
if(!x&&!y) return 0;
if(!x) { Change(y,gy); return y; }
if(!y) { Change(x,gx); return x; }
if(l==r)
{
D[x].sum=1ll*D[x].sum*(gx+D[y].sum)%Mod;
D[y].sum=1ll*D[y].sum*gy%Mod;
D[x].sum=(D[x].sum+D[y].sum)%Mod;
return x;
}
pd(x), pd(y);
int mid=(l+r)/2;
int sx=D[D[y].lc].sum, sy=D[D[x].lc].sum;
D[x].lc=Merge(D[x].lc,D[y].lc,l,mid,gx,gy);
D[x].rc=Merge(D[x].rc,D[y].rc,mid+1,r,(gx+sx)%Mod,(gy+sy)%Mod);
ps(x);
return x;
}
void DFS2(int x,int fa)
{
int mx=0;
for(auto i:q[x]) mx=max(mx,d[i]);
UpDate(root[x],0,mxd,mx);
for(ri int i=head[x];i;i=e[i].nxt)
{
int v=e[i].to;
if(v==fa) continue;
DFS2(v,x);
int w=Ask(root[v],0,d[x],0,mxd);
root[x]=Merge(root[x],root[v],0,mxd,w,0);
}
}
signed main()
{
n=read();
for(ri int i=1;i<n;i++)
{
int u,v;
u=read(), v=read();
Add(u,v), Add(v,u);
}
m=read();
for(ri int i=1;i<=m;i++)
{
int x,y;
x=read(), y=read();
q[y].eb(x);
}
DFS1(1,0), DFS2(1,0);
printf("%d\n",Ask(root[1],0,0,0,mxd));
return 0;
}
夜畔流离回,暗叹永无殿。
独隐万花翠,空寂亦难迁。
千秋孰能为,明灭常久见。
但得心未碎,踏遍九重天。