P9131 [USACO23FEB] Problem Setting P
首先我们把每道题 H 看做 E 看做
把状态相同的题目放在一起考虑,假设有
可以直接预处理。那么现在我们需要按顺序进行 DP 转移,有以下几种方法:
直接暴力
定义
分成前后两部分
这套路某年 CSP 初赛考过。
刚才的瓶颈在于枚举子集,那能不能改进一下。考虑
这样我们每次计算
for (int i=0;i<=mx;i++)
{
if (b[i]==0) continue;
int A=i>>10,B=i&1023;
ll res=1;
for (int j=0;j<1024;j++) if ((j&A)==j) (res+=dp[j][B])%=mod;
f[i]=val[i]*res%mod;
for (int j=0;j<1024;j++) if ((j&B)==B) (dp[A][j]+=f[i])%=mod;
(ans+=f[i])%=mod;
}
按 |S| 顺序
考虑按
for (int i=0;i<=m;i++)
{
for (int j=0;j<(1<<m);j++) dp[j]=0;
for (int j=0;j<(1<<m);j++)
{
if (__builtin_popcount(j)!=i) continue;
dp[j]=(1+s[j])*val[j]%mod;
(ans+=dp[j])%=mod;
}
FMT();
}
FMT 同时转移(官方做法)
考虑我们执行 FMT 的过程,比方说在做完前
我们直接定义
这样可以做到时间
for (int i=0;i<(1<<m);i++)
{
dp[i]=1;
for (int j=0;j<m;j++) if (i&(1<<j)) (dp[i]+=s[j][i^(1<<j)])%=mod;
dp[i]=dp[i]*val[i]%mod;
s[0][i]=dp[i];
(ans+=dp[i])%=mod;
for (int j=1;j<m;j++)
{
s[j][i]=s[j-1][i];
if (i&(1<<(j-1))) (s[j][i]+=s[j-1][i^(1<<(j-1))])%=mod;
}
}
分治
考虑对最高位
考虑记以
时间复杂度为
void solve(int i,int S)
{
if (i==-1)
{
Add(g[S],dp[S]);
return;
}
solve(i-1,S);
for (int T=0;T<(1<<i);T++) Add(dp[S|T|(1<<i)],g[S|T]*val[S|T|(1<<i)]%mod);
solve(i-1,S|(1<<i));
for (int T=0;T<(1<<i);T++) Add(g[S|T|(1<<i)],g[S|T]);
}