题解 [ZJOI2022] 树
一场 4 个小时多点根本不够用,T1 就没怎么看
考虑第一棵树的方案数
按顺序填点的话,一个想法是维护这个点前面有多少个点不是叶子
那么这个点的父亲就有这么多种选法
然而直接这样是不对的,会有钦定某个点不是叶子后面却没有点以它为父亲的情况
那么可以加一维记录有多少个点钦定为非叶子但还没有连边
但是复杂度变 了(或者是 ?并未细想)
那么正解:
对这个东西做容斥,容两棵树上分别有多少个点被钦定为非叶子但无儿子
动手拆一下容斥式子发现可以放在一起考虑
那么我来胡一个 dp:
令 为即将加入第 个点,左侧(不含 )有 个非叶子,右侧(包含 )有 个非叶子的方案数
转移考虑 在 中是不是叶子:
- 中为叶子:
- 中为非叶子:
- 中为叶子:
- 中为非叶子:
- 中为叶子:
- 在 中才是真的非叶子:
这个我一开始推成了:
但实际上这种情况是 本来在 T2 中为非叶子,但我把它当成叶子来统计数量了
所以应该是:
复杂度
点击查看代码
#include <bits/stdc++.h>
using namespace std;
#define INF 0x3f3f3f3f
#define N 100010
#define ll long long
//#define int long long
char buf[1<<21], *p1=buf, *p2=buf;
#define getchar() (p1==p2&&(p2=(p1=buf)+fread(buf, 1, 1<<21, stdin)), p1==p2?EOF:*p1++)
inline ll read() {
ll ans=0, f=1; char c=getchar();
while (!isdigit(c)) {if (c=='-') f=-f; c=getchar();}
while (isdigit(c)) {ans=(ans<<3)+(ans<<1)+(c^48); c=getchar();}
return ans*f;
}
int n;
ll mod;
inline void md(ll& a, ll b) {a+=b; a=a>=mod?a-mod:a;}
namespace force{
ll f[21][1<<20], rec[21][1<<20], ans[21];
void find1() {
memset(f, 0, sizeof(f));
f[1][1]=1;
for (int i=2; i<=n; ++i) {
// cout<<"i: "<<i<<endl;
int lst=i-1, lim=1<<lst;
for (int s=0; s<lim; ++s) {
for (int j=0; j<lst; ++j) {
if (s&(1<<j)) md(f[i][s^(1<<j)^(1<<i-1)], f[i-1][s]);
else md(f[i][s^(1<<i-1)], f[i-1][s]);
}
}
lim=1<<i;
for (int s=0; s<lim; ++s) md(rec[i][s], f[i][s]); //, cout<<f[i][s]<<endl;
}
}
int p[N];
void find2() {
memset(f, 0, sizeof(f));
f[1][1]=1;
for (int i=2; i<=n; ++i) {
for (int j=0; j<i; ++j) p[j]=i-1-j;
int lst=i-1, lim=1<<lst;
for (int s=0; s<lim; ++s) {
for (int j=0; j<lst; ++j) {
if (s&(1<<j)) md(f[i][s^(1<<j)^(1<<i-1)], f[i-1][s]);
else md(f[i][s^(1<<i-1)], f[i-1][s]);
}
}
lim=1<<i;
for (int s=0; s<lim; ++s) {
int t=0;
for (int j=0; j<i; ++j) if (s&(1<<j)) t|=1<<p[j];
t=(lim-1)^t;
ans[i]=(ans[i]+rec[i][t]*f[i][s])%mod;
}
}
}
void solve() {
find1(); find2();
for (int i=2; i<=n; ++i) cout<<ans[i]<<endl;
}
}
namespace task{
int now;
ll f[2][510][510];
void solve() {
for (int i=1; i<=n-1; ++i) f[now][1][i]=i;
puts("1");
for (int i=2; i<n; ++i,now^=1) {
memset(f[now^1], 0, sizeof(f[now^1]));
for (int j=1; j<=i; ++j) {
for (int k=0; k<=n-i+1; ++k) if (f[now][j][k]) {
// printf("f[%d][%d][%d]=%lld\n", i, j, k, f[now][j][k]);
if (k) f[now^1][j][k-1]=(f[now^1][j][k-1]+f[now][j][k]*j*(k-1))%mod;
f[now^1][j][k]=(f[now^1][j][k]-2*f[now][j][k]*j*k)%mod;
f[now^1][j+1][k]=(f[now^1][j+1][k]+f[now][j][k]*j*k)%mod;
// if (k) f[now^1][j][k-1]=(f[now^1][j][k-1]-f[now][j][k]*j*(k-1))%mod;
}
}
ll ans=0;
for (int j=1; j<=i; ++j) ans=(ans+j*f[now^1][j][1])%mod;
printf("%lld\n", (ans%mod+mod)%mod);
}
}
}
signed main()
{
freopen("tree.in", "r", stdin);
freopen("tree.out", "w", stdout);
n=read(); mod=read();
// for (int i=2; i<=n; ++i) printf("%lld\n", force::solve(i));
// force::solve();
task::solve();
return 0;
}
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· TypeScript + Deepseek 打造卜卦网站:技术与玄学的结合
· 阿里巴巴 QwQ-32B真的超越了 DeepSeek R-1吗?
· 【译】Visual Studio 中新的强大生产力特性
· 张高兴的大模型开发实战:(一)使用 Selenium 进行网页爬虫
· 【设计模式】告别冗长if-else语句:使用策略模式优化代码结构