题意:求i&M的popcount的和,i属于0......N
主要思路还是变加为乘。
举个例子N=22,即10110
假设M的第3位是1,分析N中:
00110
00111
00100
00101
发现其实等价于
0010
0011
0000
0001
也就是左边第4位和第5位不变,右边第1位和第2位不变拼接起来,相当于0000~0011
01110
01111
01100
01101
等价于:
0110
0111
0100
0101
相当于0100~0111
10110
10111
10100
10101
相当于1000~1010
最后把3个部分合一起就是0000~1010
如果M的第3位是0,比如说10010(二进制),其实等价于求01110这个二进制数
cpp
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const int mod=998244353;
ll ans,n,m;
int main(){
cin>>n>>m;
for(int i=0;i<60;i++){
if((m>>i)&1){
ll mask=(1ll<<i)-1;
if((n>>i)&1){
ll tmp=(n>>(i+1)<<i)|(n&mask);//左边拼接上右边
ans=(ans+tmp+1)%mod;
}
else {
ll t=(n-(1ll<<i))|mask;//先把n更新一下,注意要把右边变成最大值
ll tmp=(t>>(i+1)<<i)|(t&mask);//同样处理
ans=(ans+tmp+1)%mod;
}
}
}
cout<<ans;
}
题意:共n把钥匙m次测试,至少k把钥匙才打开门,问满足所有测试条件的真钥匙的组合种类
位元枚举:用位元表示所有真钥匙的组合,然后保存每个测试的钥匙状态,满足所有测试就是答案组合
cpp
#include <bits/stdc++.h>
using namespace std;
int keys[20];
char r[105];
int f(int x){
int ct=0;
while(x){
if(x&1)ct++;x>>=1;
}
return ct;
}
int main(){
int n,m,k;cin>>n>>m>>k;//n把钥匙,m个测试,至少k把钥匙才能打开门
for(int i=0;i<m;i++){
int c;cin>>c;
while(c--){
int a;cin>>a;
keys[i]|=1<<(a-1);//把i测试用的钥匙存起来
}
cin>>r[i];//最终门的状态
}
int ans=0;
for(int s=0;s<(1<<n);s++){//枚举所有正确钥匙的组合情况
bool er=0;
for(int i=0;i<m;i++){//看看是否满足所有的测试用例
if((f(keys[i]&s)>=k&&r[i]!='o')||(f(keys[i]&s)<k&&r[i]=='o')){
er=1;break;
}
}
ans+=!er;
}cout<<ans;
}
不难发现:10101010101010101010 = 10*(1010101010101010101)
就是10*(10^0+10^2+10^4+10^6+10^8+10^10+10^12+10^14+10^16+10^18) 进行等比求和:
10*(10^20-1)/(10^2-1)
那么输入一个N,我们计算出其长度len
就是:N*(10^0+10^len+10^len*2+10^len*3+......+10^len*n-1)
就是N*(10^len*n)/(10^len-1)
然后知识点:a/b%mod=a*b^(mod-2)%mod
cpp
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const int MOD=998244353;
ll f(ll x,ll y){
x%=MOD;
ll res=1;
while(y){
if(y&1)res=res*x%MOD;
y>>=1;x=x*x%MOD;
}
return res;
}
ll inv(ll x){
return f(x,MOD-2);
}
int main(){
ll n;cin>>n;
int len=(to_string(n)).size();
cout<<n%MOD * (f(f(10,n),len)-1)%MOD * inv(f(10,len)-1)%MOD<<'\n';
}
题意:一共有n个店,每个店有许多口味,问能尝到所有口味至少要去多少家店
位元枚举:用位元来表示所有店的组合,并保存每个店提供的口味状态,如果某个组合中可提供所有的口味就行
cpp
#include <bits/stdc++.h>
using namespace std;
int d[10];
int main(){
int n,m;
cin>>n>>m;//n个店,m种口味
for(int i=0;i<n;i++){
string s;cin>>s;
for(char c:s){
d[i]=(d[i]<<1)+(c=='o');//保存第i个店卖的口味状态
}
}
int an=100;
for(int i=0;i<(1<<n);i++){//枚举所有的店的组合
int now=0,cnt=0;
for(int j=0;j<n;j++){
if((i>>j)&1)now|=d[j],cnt++;//把组合中的每个店卖的东西都合并起来
}
if(now==(1<<m)-1)an=min(an,cnt);//如果当好覆盖了所有口味,那就更新一次答案
}
cout<<an;
}