题解 [AGC009E] Eternal Average
一开始想把贡献拆成被操作了 次的数贡献
然后就什么也不会了
正解首先需要一个神奇的转化
考虑每个操作方案都对应着一棵 叉树
其中有 个权值为 0 的叶子,有 个权值为 1 的叶子
其它每个非叶节点的权值是其儿子权值的平均值
这样根节点的权值就是最后剩下的那个数
考虑一棵 叉树合法的条件是什么
令 为每个 0 节点的深度, 为每个 1 节点的深度
考虑若将所有 0 节点的权值改为 1,则根节点权值一定为 1
此时满足
容易发现这是必要条件
考虑其充分性
因为最后和为 1,考虑 进制小数的进位
发现每个深度的节点都完成了凑齐 个节点形成一个上层节点的过程
那么确实是充分的
那么现在一个根节点的权值 合法的条件就变成了:
可以被表示成 的形式且 可以被表示成 的形式
和 好像反了但是没影响
- 与固定分数 的幂次相关的限制可以考虑 进制小数 DP?
注意小数 DP 需要记录最后一位是不是 0
尝试对 的 进制小数形式 进行 DP
那么限制条件是什么呢?
如果不考虑进位有 ,加上进位就是
和 好像也反了但是问题不大
同理有
对这个东西大力小数 DP 即可
复杂度
点击查看代码
#include <bits/stdc++.h>
using namespace std;
#define INF 0x3f3f3f3f
#define N 4010
#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 int read() {
int 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, m, k;
const ll mod=1e9+7;
ll f[N][N][2], ans;
signed main()
{
n=read(); m=read(); k=read();
int lim=(n+m-1)/(k-1);
f[0][0][0]=1;
for (int i=0; i<=lim; ++i) {
for (int j=0; j<=min((k-1)*i, n); ++j) {
// if (f[i][j][0]) printf("f[%d][%d][%d]=%lld\n", i, j, 0, f[i][j][0]);
// if (f[i][j][1]) printf("f[%d][%d][%d]=%lld\n", i, j, 1, f[i][j][1]);
for (int t=0; t<k; ++t) {
if (j+t<=n && 1+(i+1)*(k-1)-j-t<=m) f[i+1][j+t][t!=0]=(f[i+1][j+t][t!=0]+f[i][j][0]+f[i][j][1])%mod;
}
}
}
for (int i=1; i<=lim; ++i)
for (int j=0; j<=min((k-1)*i, n); ++j)
if (j%(k-1)==n%(k-1) && ((1-j)%(k-1)+(k-1))%(k-1)==m%(k-1))
ans=(ans+f[i][j][1])%mod;
printf("%lld\n", ans);
return 0;
}
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 无需6万激活码!GitHub神秘组织3小时极速复刻Manus,手把手教你使用OpenManus搭建本
· Manus爆火,是硬核还是营销?
· 终于写完轮子一部分:tcp代理 了,记录一下
· 别再用vector<bool>了!Google高级工程师:这可能是STL最大的设计失误
· 单元测试从入门到精通