[USACO23FEB] Problem Setting P 题解
[USACO23FEB] Problem Setting P
题目说的很绕,意思就是所有验题人都认为题目难度顺序单增。
发现 H
看作 E
看作
而对于单增的限制条件,我们转化为:对于所有相邻的两道题
于是设
这就是能选的状态集合。至于当前
最后的
这个方法的复杂度是
#include<bits/stdc++.h>
using namespace std;
using ll=long long;
constexpr int MAXN=1e5+5,MAXM=1<<20,MOD=1e9+7;
int n,m,num[MAXN],c[MAXM],f[MAXN],ans;
int fac[MAXN],inv[MAXN];
int g[MAXM][21];
string s;
void add(int&x,int y){
x=x+y>=MOD?x+y-MOD:x+y;
}
int power(ll a,int b){
ll res=1;
for(;b;a=a*a%MOD,b>>=1)if(b&1)res=res*a%MOD;
return res;
}
void init(){
fac[0]=1;
for(int i=1;i<=n;++i) fac[i]=(ll)fac[i-1]*i%MOD;
inv[n]=power(fac[n],MOD-2);
for(int i=n-1;~i;--i) inv[i]=(ll)inv[i+1]*(i+1)%MOD;
}
int calc(int ci){
int res=0;
for(int i=0;i<ci;++i)
add(res,(ll)fac[ci]*inv[i]%MOD);
return res;
}
int main(){
ios::sync_with_stdio(0);
cin.tie(nullptr),cout.tie(nullptr);
cin>>n>>m;
init();
for(int i=1;i<=m;++i){
cin>>s;
for(int j=0;j<n;++j)
if(s[j]=='H')
num[j+1]|=1<<(i-1);
}
for(int i=1;i<=n;++i) ++c[num[i]];
f[0]=calc(c[0]);
for(int i=1;i<=m;++i) g[0][i]=f[0];
ans=f[0];
for(int i=1,B=1<<m,v;i<B;++i){
v=1;
for(int j=1;j<=m;++j)
if(i&(1<<(j-1)))
add(v,g[i^(1<<(j-1))][j]);
f[i]=(ll)v*calc(c[i])%MOD;
add(ans,f[i]);
g[i][1]=f[i];
for(int j=1;j<m;++j){
g[i][j+1]=g[i][j];
if(i&(1<<(j-1))) add(g[i][j+1],g[i^(1<<(j-1))][j]);
}
}
cout<<ans<<'\n';
return 0;
}
考虑满分做法。引入辅助数组
所以原本的
当然对于
- 若
的 位为 ,则 ; - 否则
,其中 意思是 去掉最大的元素后组成的集合。
#include<bits/stdc++.h>
using namespace std;
using ll=long long;
constexpr int MAXN=1e5+5,MAXM=1<<20,MOD=1e9+7;
int n,m,num[MAXN],c[MAXM],f[MAXN],ans;
int fac[MAXN],inv[MAXN];
int g[MAXM][21];
string s;
void add(int&x,int y){
x=x+y>=MOD?x+y-MOD:x+y;
}
int power(ll a,int b){
ll res=1;
for(;b;a=a*a%MOD,b>>=1)if(b&1)res=res*a%MOD;
return res;
}
void init(){
fac[0]=1;
for(int i=1;i<=n;++i) fac[i]=(ll)fac[i-1]*i%MOD;
inv[n]=power(fac[n],MOD-2);
for(int i=n-1;~i;--i) inv[i]=(ll)inv[i+1]*(i+1)%MOD;
}
int calc(int ci){
int res=0;
for(int i=0;i<ci;++i)
add(res,(ll)fac[ci]*inv[i]%MOD);
return res;
}
int main(){
ios::sync_with_stdio(0);
cin.tie(nullptr),cout.tie(nullptr);
cin>>n>>m;
init();
for(int i=1;i<=m;++i){
cin>>s;
for(int j=0;j<n;++j)
if(s[j]=='H')
num[j+1]|=1<<(i-1);
}
for(int i=1;i<=n;++i) ++c[num[i]];
f[0]=calc(c[0]);
for(int i=1;i<=m;++i) g[0][i]=f[0];
ans=f[0];
for(int i=1,B=1<<m,v;i<B;++i){
v=1;
for(int j=1;j<=m;++j)
if(i&(1<<(j-1)))
add(v,g[i^(1<<(j-1))][j]);
f[i]=(ll)v*calc(c[i])%MOD;
add(ans,f[i]);
g[i][1]=f[i];
for(int j=1;j<m;++j){
g[i][j+1]=g[i][j];
if(i&(1<<(j-1))) add(g[i][j+1],g[i^(1<<(j-1))][j]);
}
}
cout<<ans<<'\n';
return 0;
}
即得易见平凡,仿照上例显然。留作习题答案略,读者自证不难。
反之亦然同理,推论自然成立。略去过程 ,由上可知证毕。
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 震惊!C++程序真的从main开始吗?99%的程序员都答错了
· winform 绘制太阳,地球,月球 运作规律
· 【硬核科普】Trae如何「偷看」你的代码?零基础破解AI编程运行原理
· 上周热点回顾(3.3-3.9)
· 超详细:普通电脑也行Windows部署deepseek R1训练数据并当服务器共享给他人