1395C - Boboniu and Bit Operations 状压dp 或 思维
先说状压dp的做法,f[i][j]表示已经求出了c[i],前i个c[i]是否可以或成j,我们枚举a,枚举b,枚举当前状态也就是前i个c[i]或的和,然后枚举a[i]&b[j]的子集也就是c[i]的子集,看看是否可以转移到f[i][j],由于是枚举子集,所以时间复杂度会减少很多
C. Boboniu and Bit Operations(状压)_issue是fw的博客-CSDN博客
#include <bits/stdc++.h>
using namespace std;
#define ll long long
#define lowbit(i) ((i)&(-i))
const ll mod=9999973;
const int N = 2e5+5;
ll n,m,a[205],b[205],f[205][1<<10];
int main(){
scanf("%lld%lld",&n,&m);
for(int i=1;i<=n;i++) scanf("%lld",&a[i]);
for(int j=1;j<=m;j++) scanf("%lld",&b[j]);
f[0][0]=1;
for(int i=1;i<=n;i++)
for(int j=1;j<=m;j++){
ll v=a[i]&b[j];
for(int u=0;u<(1<<9);u++){
if((u&v)!=v) continue;//v中的1 u应该都有
for(int w=v;w;w=(w-1)&v)
f[i][u]|=f[i-1][u^w];
f[i][u]|=f[i-1][u];
}
}
for(int i=0;;i++)
if(f[n][i]){printf("%d ",i);break;}
system("pause");
return 0;
}
思维的做法:因为或运算只会让数不变或增加,所以最优的状态应该是不变,所以我们应该让c[i]都相同且尽量最小,如果c[i]不相同的话或出来的值会大于所有的c[i]这显然是不行的,所以最小值应该会出现在所有c[i]都相同的情况下,那我们就枚举测试每个答案行不行就可以了,因为数据范围小也用不到二分
C. Boboniu and Bit Operations(位运算操作+思维)Codeforces Round #664 (Div. 2)_unique_pursuit的博客-CSDN博客
ll n,m,a[205],b[205];
bool check(ll s){
for(int i=1;i<=n;i++){
ll flag=0;
for(int j=1;j<=m;j++){
ll tmp=a[i]&b[j];
if((tmp|s)==s) flag=1;
if(flag) break;
}
if(!flag) return false;
}
return true;
}
int main(){
scanf("%lld%lld",&n,&m);
for(int i=1;i<=n;i++) scanf("%lld",&a[i]);
for(int i=1;i<=m;i++) scanf("%lld",&b[i]);
for(int i=0;i<(1<<10);i++){
if(check(i)){printf("%d",i);break;}
}
system("pause");
return 0;
}
[J-Melborp Elcissalc_"蔚来杯" dp 前缀和小技巧](ac.nowcoder.com/acm/contest… "J-Melborp Elcissalc_"蔚来杯" dp 前缀和小技巧")
这个题也是用到了之前用到的前缀和技巧,但是比之前那个好像更难了点,对于一个前缀和来说,要求有多少区间的和是k的倍数,那我们可以对前缀和数组的每个数都取余k,之后每个数出现了x次就有C(x,2)个区间是k的倍数,0例外是C(x+1,2),因为1个0也满足条件;对于一个数组我们需要知道这个数组用到了0-k-1中的哪些数,这些数出现了多少次以及一共有多少区间符合条件,那我们就设一个dp[i][len][s]表示前i个数中用了len个,可以产生s个符合条件的区间,枚举到i,枚举想要用num个i,那么转移方程就是dp[i][len+num][s+C(num,2)]=dp[i-1][len][s],要是放0的话因为例外所以要是dp[i][len+num][s+C(num+1,2)],代码应该是因为方便转移将i都加了1位
2022牛客暑期多校训练营7 个人题解 更新至5题 - 知乎 (zhihu.com)
ll C(ll x,ll y){//求组合数模板
return fac[x]*getinv(fac[y],mod)%mod*getinv(fac[x-y],mod)%mod;
}
ll n,k,t,dp[70][70][3005],c[105][105];
int main(){
fac[0]=1;
for(ll i=1;i<=70;i++) fac[i]=fac[i-1]*i%mod;
for(ll i=0;i<=64;i++)
for(ll j=i;j<=65;j++)
c[j][i]=C(j,i);
scanf("%lld%lld%lld",&n,&k,&t);
dp[0][0][0]=1;
for(int i=1;i<=k;i++)
for(int len=0;len<=n;len++)
for(int s=0;s<=t;s++)
if(dp[i-1][len][s])
for(int num=0;num+len<=n;num++){
if(i==1) dp[i][num+len][c[num+1][2]+s]=(dp[i][num+len][c[num+1][2]+s]+dp[i-1][len][s]*c[num+len][num]%mod)%mod;
else dp[i][num+len][c[num][2]+s]=(dp[i][num+len][c[num][2]+s]+dp[i-1][len][s]*c[num+len][num]%mod)%mod;
}
printf("%lld",dp[k][n][t]);
system("pause");
return 0;
}
P1896 [SCOI2005] 互不侵犯 - 洛谷 | 状态压缩dp
之前做过的一道题,当时的思想没有跟上现在来重新做一遍,这其实很像n皇后问题,之前是用搜索做,现在是用代码更简单的dp来做,用一个二进制数来表示一行的放置情况,预处理出所有合法情况之后枚举每行的情况,和上一行作比较如果可行就枚举已经放了多少个国王来进行转移
ll n,m,dp[10][1<<9][105];
ll ok[1<<9],cnt=0;
int main(){
scanf("%lld%lld",&n,&m);
for(int i=0;i<1<<n;i++){
if(i&i>>1||i&i<<1) continue;
ok[++cnt]=i;
}
dp[0][1][0]=1;
for(int i=1;i<=n;i++)
for(int j=1;j<=cnt;j++)
for(int k=1;k<=cnt;k++){
if(ok[j]&ok[k]||ok[j]>>1&ok[k]||ok[j]<<1&ok[k]) continue;
ll x=__builtin_popcount(ok[j]),y=__builtin_popcount(ok[k]);
for(int l=y;l<=m-x;l++) dp[i][j][l+x]+=dp[i-1][k][l];
//第k种情况已经有y个了,前i-2行可能也有放置的国王,所以从y开始枚举
}
ll ans=0;
for(int i=1;i<=cnt;i++) ans+=dp[n][i][m];
printf("%lld\n", ans);
system("pause");
return 0;
}