HNOI2011 卡农 题解
真·状压dp
题面
啥比题面
等价于在 中选 个数(无序),满足:
- 两两不同
- 异或和为 .
问方案数,对 取模 .
题解
这个无序是个假的,最后除掉 就完了,,,
设 表示选了 个数的方案数 .
考虑容斥,显然如果前 个数都确定了,则第 个数也就确定了(第 个数为前面数的异或和)
于是直接大力选,方案数为 .
然后可能违反限制,我们算一下:
- 前面 个数的异或和为 ,此时我们的第 个数为 是不在 中的,所以排掉,方案数 .
- 存在相同:设 两位置存在相同,则丢掉它们俩异或和不变(为 ), 的取值显然有 种,选 的位置有 种,所以总方案数为
减掉,于是得到最终转移方程 .
因为 的底是不变的,所以可以预处理下降幂,然后可以先算出 然后转移的时候直接用 .
这样就可以 转移了,总复杂度 (迫真, 是快速幂友情赠送) .
代码
using namespace std;
typedef long long ll;
const int N = 1e6+500, P = 1e8+7;
int n, m;
ll dpow[N], N2;
ll dp[N];
ll qpow(ll a, ll n)
{
ll ans = 1;
while (n)
{
if (n&1) ans = ans * a % P;
a = a * a % P; n >>= 1;
} return ans;
}
void init()
{
N2 = qpow(2, n) - 1;
dpow[0] = 1;
for (int i=1; i<=m; i++) dpow[i] = dpow[i-1] * (N2-i+1) % P;
}
inline ll inv(const ll& x){return qpow(x, P-2) % P;}
inline ll fac(const int& x){return x ? fac(x-1) * x % P : 1;}
int main()
{
#ifndef ONLINE_JUDGE
freopen("i.in", "r", stdin);
#endif
scanf("%d%d", &n, &m); init();
dp[0] = 1;
for (int i=2; i<=m; i++)
dp[i] = ((dpow[i-1] - dp[i-1] + P) % P - dp[i-2] * (i-1) % P * (N2 - (i-2)) % P + P) % P;
printf("%lld\n", dp[m] * inv(fac(m)) % P);
return 0;
}
以下是博客签名,正文无关
本文来自博客园,作者:yspm,转载请注明原文链接:https://www.cnblogs.com/CDOI-24374/p/15813340.html
版权声明:本作品采用「署名-非商业性使用-相同方式共享 4.0 国际」许可协议(CC BY-NC-SA 4.0)进行许可。看完如果觉得有用请点个赞吧 QwQ
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 地球OL攻略 —— 某应届生求职总结
· 周边上新:园子的第一款马克杯温暖上架
· Open-Sora 2.0 重磅开源!
· 提示词工程——AI应用必不可少的技术
· .NET周刊【3月第1期 2025-03-02】