[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;
}
posted @ 2021-05-10 19:38  zkdxl  阅读(44)  评论(0编辑  收藏  举报