【题解】CF2229E

0

\(link\)

1.重构树

注意到 \(n\) 一定在序列末尾

\(n\) 为根

2.发现dp

发现 \(S\) 序列单增

启发我们序列扫描同时 \(dp\) 转移

3.转移条件

定义 \(mx(u):u\) 子树除 \(u\) 外节点编号最大值

\(j\) 能由 \(i\) 转移,当且仅当 \(mx(j)<i<j\)

\(n\) 要分类讨论,即记录 \([1,n-1]\) 每个数是否可达 \(n\)

4.转移方程

\(dp_i:S\) 末尾为 \(i\) 时方案数

\(dp_i=\sum_{j=mx[i]+1}^{i-1}dp_j\)

5.答案计算

\(ans=\sum_{i=1}^{n-1}dp_i*[i可达n]\)

6.代码

// Problem: E. Deconstruction Tree
// Contest: Codeforces - Spectral::Cup 2026 Round 2 (Codeforces Round 1100, Div. 1 + Div. 2)
// URL: https://codeforces.com/contest/2229/problem/E
// Memory Limit: 256 MB
// Time Limit: 2000 ms
// 
// Powered by CP Editor (https://cpeditor.org)

#include<bits/stdc++.h>
#define int long long 
#define Pair pair<int,int>
#define eps 1e-6
using namespace std;
using LL=long long;
const int N=5e5+10,P=998244353;
vector<int> mp[N];
int mx[N];
void Mx(int u,int pa){
	mx[u]=0;
	for(auto v:mp[u]){
		if(v==pa) continue;
		Mx(v,u);
		mx[u]=max({mx[u],mx[v],v});
	}
}
set<int,greater<int> > outs;
vector<int> ins;
void sontree(int u,int pa){
	outs.erase(u);
	ins.push_back(u);
	for(auto v:mp[u]){
		if(v==pa) continue;
		sontree(v,u);
	}
}
int ok[N];
int dp[N];
int tree[N];
void update(int x,int d){
	while(x<N){
		tree[x]+=d;
		tree[x]%=P;
		x+=x&-x;
	}
}
int query(int x){
	int res=0;
	while(x){
		res+=tree[x];
		res%=P;
		x-=x&-x;
	}
	return res;
}
int fquery(int l,int r){
	if(l>r) return 0;
	return ((query(r)-query(l-1))%P+P)%P;
}
void solve(){
	int n;cin>>n;
	outs.clear();ins.clear();
	for(int i=1;i<=n;i++){
		mp[i].clear();
		mx[i]=0;
		ok[i]=0;
		dp[i]=0;
		tree[i]=0;
	}
	for(int i=1;i<n;i++){
		int a,b;cin>>a>>b;
		mp[a].push_back(b);
		mp[b].push_back(a);
	}
	
	if(mp[n].size()<=1){
		cout<<1<<"\n";
		return ;
	}
	
	Mx(n,0);
	
	outs.clear();for(int i=1;i<n;i++) outs.insert(i);
	for(auto u:mp[n]){
		sontree(u,n);
		for(auto v:ins){
			if(v>*outs.begin()) ok[v]=1;
		}
		for(auto v:ins) outs.insert(v);
		ins.clear();
	}
	
	int tp=-1;
	
	for(int i=1;i<n;i++){
		if(mp[i].size()==1) tp=i;
	}
	
	dp[tp]=1;update(tp,1);
	
	for(int i=tp+1;i<n;i++){
		dp[i]=fquery(mx[i]+1,i-1)%P;
		update(i,dp[i]);
		//cout<<i<<" "<<dp[i]<<"\n";
	}
	
	int ans=0;
	for(int i=tp;i<n;i++){
		if(ok[i]) ans+=dp[i];
		ans%=P;
	}
	cout<<ans<<"\n";
}
signed main(){
	ios::sync_with_stdio(0);cin.tie(0);cout.tie(0);
	int t=1;
	cin>>t;
	while(t--) solve();
	return 0;
}
//i->j
//i<j
//i>mx[j]
//从j考虑
//可以从[mx[j]+1,j-1]转移得到

7.启发

7.1 比赛

打CF不要摆烂,万一会呢

7.2 本题

  • 一个看似不能dp的问题,转化一下,是可以dp的

  • 一个树上求方案数,并不一定要用传统的树形dp求解,可以分析性质,转化成线性dp

  • 必要时要分讨

posted @ 2026-05-26 00:06  Aistyr  阅读(32)  评论(0)    收藏  举报