牛客IOI周赛27-提高组 C.马老师(容斥dp)
Posted issue是fw
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了牛客IOI周赛27-提高组 C.马老师(容斥dp)相关的知识,希望对你有一定的参考价值。
如果在第 i i i个位置出现了第 j j j位二进制 1 1 1,那么 [ i , n ] [i,n] [i,n]都有第 j j j位二进制 1 1 1
于是定义 f [ i ] [ j ] f[i][j] f[i][j]表示当前考虑前 i i i位二进制,数列和为 j j j的方案数
有 f [ i ] [ j ] + = ∑ r = 1 n f [ i − 1 ] [ j − r ∗ ( 1 < < i ) ] f[i][j]+=\\sum\\limits_{r=1}^{n}f[i-1][j-r*(1<<i)] f[i][j]+=r=1∑nf[i−1][j−r∗(1<<i)]
暴力转移复杂度当然还是很高,注意到
f [ i ] [ j ] f[i][j] f[i][j]和 f [ i ] [ j − ( 1 < < i ) ] f[i][j-(1<<i)] f[i][j−(1<<i)]的转移方程式非常相似,所以我们直接先令
Ⅰ. f [ i ] [ j ] = f [ i ] [ j − ( 1 < < i ) ] f[i][j]=f[i][j-(1<<i)] f[i][j]=f[i][j−(1<<i)]
然后考虑 j j j还可以从上一轮的 j − ( 1 < < i ) j-(1<<i) j−(1<<i)状态转移而来,所以
Ⅱ. f [ i ] [ j ] + = f [ i − 1 ] [ j − ( 1 < < i ) ] f[i][j]+=f[i-1][j-(1<<i)] f[i][j]+=f[i−1][j−(1<<i)]
特别的,如果 j > = ( n + 1 ) ∗ ( 1 < < i ) j>=(n+1)*(1<<i) j>=(n+1)∗(1<<i),那么其实是算多了的
因为最多从 j − n ∗ ( 1 < < i ) j-n*(1<<i) j−n∗(1<<i)处转移而来,所以此时减去算多的部分,也就是
Ⅲ. f [ i ] [ j ] − = f [ i − 1 ] [ j − ( n + 1 ) ∗ ( 1 < < i ) ] f[i][j]-=f[i-1][j-(n+1)*(1<<i)] f[i][j]−=f[i−1][j−(n+1)∗(1<<i)]
最后,不要忘记继承上一轮的状态,因为可以不选这个二进制
Ⅳ. f [ i ] [ j ] + = f [ i − 1 ] [ j ] f[i][j]+=f[i-1][j] f[i][j]+=f[i−1][j]
#include <bits/stdc++.h>
using namespace std;
const int mod = 1e9+7;
using in = long long;
const int maxn = 5e6+10;
int n,m,f[2][maxn];
void upd(int &x,int y){ x = (x+y)%mod; }
signed main()
{
cin >> n >> m;
for(int i=0;i<=m && i<=n;i++) f[0][i] = 1;
for(int i=1;i<=23;i++)//考虑每个二进制放的位置
{
int t = i&1, v = t^1;
long long lim = (n+1)*(1ll<<i);
for(int j=0;j<=m;j++)
{
f[t][j] = 0;
if( j>=(1ll<<i) )
{
upd( f[t][j],f[t][j-(1ll<<i)] );
upd( f[t][j],f[v][j-(1ll<<i)] );
}
if( j>=lim )
upd( f[t][j],-f[v][j-lim] );
}
for(int j=0;j<=m;j++) upd( f[t][j],f[v][j] );
}
cout << ( f[23&1][m]%mod+mod )%mod;
}
以上是关于牛客IOI周赛27-提高组 C.马老师(容斥dp)的主要内容,如果未能解决你的问题,请参考以下文章