洛谷 P5281 [ZJOI2019]Minmax搜索
https://www.luogu.com.cn/problem/P5281
Tutorial
https://www.luogu.com.cn/blog/1641129366cjy/p5281-zjoi2019minimax-sou-suo
求恰好稳定度为(w)的集合,差分后变为,求稳定度(le w)的集合;减法原理后变为,求稳定度(>w)的集合.
将(W)到根的路径称为答案链,那么需要保证在每个叶子的权值最多改变(w)的情况下,这条链上的每个节点的权值不变.
那么我们可以对每个答案链上的节点的非链上下一个点的儿子的子树分开考虑.
那么就有两种情况,子树的父亲是奇点/偶点,由于类似,接下来只考虑奇度点的情况.
由于奇点是取max,所以需要保证最后子树的根的权值(le W),设(dp(u))表示(u)的权值(le W)的选择叶子集合的方案数.
对于叶子,有(dp(u)=[u le W]+[u+wle W]).
对于奇点,有(dp(u)=prod dp(v))
对于偶点,有(dp(u)=cnt_u-prod(cnt_v-dp(v))),其中(cnt_u)表示(u)子树中选择叶子集合的方案总数.
发现,假如对于所有奇点或偶点,令(cnt_u-dp(u))为新的(dp(u)),那么所有非叶节点的转移变为了(dp(u)=cnt_u-prod dp(v)),由于最后我们统计的答案为所有子树根节点的(dp(u))的积,所以令深度奇偶性与子树根节点不同的点如此变换,即可达到简化的目的.
可以发现,随着(w)的不断减少(/增加),叶子的(dp)值会改变一次,然后影响它到其子树的根的路径上的(dp)值,这就是动态DP解决的问题.
我们选择令(w)不断减少,因为之前我们做的变换的关系,若不断增加可能会发生叶子的(dp)值从(0)变为(1)的情况.而令(w)不断减少就可以避免这个问题.
Code
#include <cstdio>
#include <cstring>
#include <iostream>
#include <vector>
#define debug(...) fprintf(stderr,__VA_ARGS__)
#define inver(a) power(a,mod-2)
#define lson u<<1,l,mid
#define rson u<<1|1,mid+1,r
using namespace std;
inline char gc() {
// return getchar();
static char buf[100000],*l=buf,*r=buf;
return l==r&&(r=(l=buf)+fread(buf,1,100000,stdin),l==r)?EOF:*l++;
}
template<class T> void rd(T &x) {
x=0; int f=1,ch=gc();
while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=gc();}
while(ch>='0'&&ch<='9'){x=x*10-'0'+ch;ch=gc();}
x*=f;
}
template<class T> inline bool Cmax(T &x,T y) {return x<y?x=y,1:0;}
template<class T> inline bool Cmin(T &x,T y) {return x>y?x=y,1:0;}
typedef long long ll;
const int mod=998244353;
const int inf=1e9;
const int maxn=2e5+50;
int n,L,R;
int head[maxn];
int anc[maxn],dep[maxn],siz[maxn],son[maxn],val[maxn],cnt[maxn];
int top[maxn],bot[maxn],dfn[maxn],rnk[maxn],root[maxn],dfc;
int dp[maxn];
int now,an[maxn];
vector<int> rec[maxn];
inline int sub(int x) {return x<0?x+mod:x;}
ll power(ll x,ll y) {
ll re=1;
while(y) {
if(y&1) re=re*x%mod;
x=x*x%mod;
y>>=1;
}
return re;
}
struct edge {
int to,nex;
edge(int to=0,int nex=0):to(to),nex(nex){}
};
vector<edge> G;
struct func {
int k,b;
func(int k=1,int b=0):k(k),b(b){}
inline int F(int x) {return ((ll)k*x+b)%mod;}
inline friend func merge(func a,func b) {
return func((ll)a.k*b.k%mod,((ll)a.k*b.b+a.b)%mod);
}
} f[maxn];
inline void addedge(int u,int v) {
G.push_back(edge(v,head[u])),head[u]=G.size()-1;
G.push_back(edge(u,head[v])),head[v]=G.size()-1;
}
void dfs0(int u) {
siz[u]=cnt[u]=1;
val[u]=dep[u]?-inf:inf;
for(int i=head[u];~i;i=G[i].nex) {
int v=G[i].to; if(v==anc[u]) continue;
anc[v]=u;
dep[v]=dep[u]^1;
dfs0(v);
cnt[u]=(ll)cnt[u]*cnt[v]%mod;
siz[u]+=siz[v];
if(siz[v]>siz[son[u]]) son[u]=v;
if(dep[u]) Cmax(val[u],val[v]); else Cmin(val[u],val[v]);
}
if(siz[u]==1) val[u]=u,cnt[u]=2;
}
void dfs1(int u,int chain,int rt) {
root[u]=rt;
top[u]=chain,bot[chain]=u;
rnk[dfn[u]=++dfc]=u;
if(siz[u]==1) {
if(dep[rt]==0) {
dp[u]=u<=val[1]; if(dep[u]^dep[root[u]]) dp[u]=2-dp[u];
if(u<=val[1]) rec[val[1]-u].push_back(u);
}
else {
dp[u]=u>=val[1]; if(dep[u]^dep[root[u]]) dp[u]=2-dp[u];
if(u>=val[1]) rec[u-val[1]].push_back(u);
}
return;
}
dfs1(son[u],chain,rt);
int k=mod-1;
for(int i=head[u];~i;i=G[i].nex) {
int v=G[i].to; if(v==anc[u]||v==son[u]) continue;
dfs1(v,v,rt);
k=(ll)k*dp[v]%mod;
}
f[u]=func(k,cnt[u]);
dp[u]=f[u].F(dp[son[u]]);
}
void dfs2(int u) {
for(int i=head[u];~i;i=G[i].nex) {
int v=G[i].to; if(v==anc[u]) continue;
if(val[v]==val[u]) dfs2(v);
else {
dfs1(v,v,v);
now=(ll)now*dp[v]%mod;
}
}
}
namespace seg {
const int maxnode=maxn<<2;
func a[maxnode];
inline void pushup(int u) {
a[u]=merge(a[u<<1],a[u<<1|1]);
}
void build(int u,int l,int r) {
if(l==r) {
a[u]=f[rnk[l]];
return;
}
int mid=(l+r)>>1;
build(lson);
build(rson);
pushup(u);
}
void update(int u,int l,int r,int qp) {
if(l==r) {
a[u]=f[rnk[l]];
return;
}
int mid=(l+r)>>1;
if(qp<=mid) update(lson,qp);
else update(rson,qp);
pushup(u);
}
func query(int u,int l,int r,int ql,int qr) {
if(l==ql&&r==qr) {
return a[u];
}
int mid=(l+r)>>1;
if(qr<=mid) return query(lson,ql,qr);
else if(ql>mid) return query(rson,ql,qr);
else {
func L=query(lson,ql,mid);
func R=query(rson,mid+1,qr);
return merge(L,R);
}
}
}
void update(int u) {
now=(ll)now*inver(dp[root[u]])%mod;
dp[u]=2; if(dep[u]^dep[root[u]]) dp[u]=0;
while(true) {
u=top[u];
if(u!=root[u]&&u!=bot[u]) f[anc[u]].k=(ll)f[anc[u]].k*inver(dp[u])%mod;
if(u!=bot[u]) {
dp[u]=seg::query(1,1,dfc,dfn[u],dfn[bot[u]]-1).F(dp[bot[u]]);
}
if(u!=root[u]) {
f[anc[u]].k=(ll)f[anc[u]].k*dp[u]%mod;
seg::update(1,1,dfc,dfn[anc[u]]);
u=anc[u];
}
else break;
}
now=(ll)now*dp[u]%mod;
}
int main() {
rd(n),rd(L),rd(R);
memset(head,-1,sizeof(head));
for(int i=1;i<n;++i) {
int u,v; rd(u),rd(v);
addedge(u,v);
}
dep[1]=1,dfs0(1);
now=1,dfs2(1);
an[n]=sub(cnt[1]-1);
seg::build(1,1,dfc);
for(int i=n-1;i>=1;--i) {
for(int j=0;j<rec[i].size();++j) update(rec[i][j]);
an[i]=sub(cnt[1]-now);
}
for(int i=L;i<=R;++i) {
if(i!=L) printf(" ");
printf("%d",sub(an[i]-an[i-1]));
}
printf("
");
return 0;
}