bitset
起因是在做CF1659C时看到了用bitset的暴力写法,想到我还不会bitset,遂补之。
简单来说,bitset可以看成一个bool数组,bitset<64> str就可以表示一个长度为64的01串。想要找第i位直接cout<<str[i]即可。值得注意的是这里计数是从右向左的,因此对于一个bitset<4> str=1,输出后结果为0001,并且str[0]=1,str[1到3]=0;
bitset支持各种位运算操作,还有许多函数
对于一个叫做st的bitset:
st.size() 返回大小(位数)
st.count() 返回1的个数
st.any() 返回是否有1
st.none() 返回是否没有1
st.set() 全都变成1
st.set(p) 将第p + 1位变成1
st.set(p, x) 将第p + 1位变成x
st.reset() 全都变成0
st.reset(p) 将第p + 1位变成0
st.flip() 全都取反
st.flip(p) 将第p + 1位取反
st.to_ulong() 返回它转换为unsigned long的结果,如果超出范围则报错
st.to_ullong() 返回它转换为unsigned long long的结果,如果超出范围则报错
st.to_string() 返回它转换为string的结果
好了现在你已经学会bitset的基础操作了,我们来看这些题目吧!
CF1659C

首先,对于所有的路径,横着需要移动m-1次,竖着需要移动n-1次。算上一开始的(1,1),路径长度固定为n+m-1次。
想让1和-1全部抵消路径长度必须是偶数,因此奇数可以直接输出no
对于偶数的情况,可以想到,如果要总和为0,那路径上的1的数量一定是和m相等的。1的数量也就是(n+m-1)/2.
因此我们只要知道:假如有一条路径经过了正好(n+m-1)/2个1,就是yes
我们用f[i][j][k]表示到达(i,j)时,经过正好k个1的路径是否存在。例如bitset 为100,就说明经过0个1和1个1的情况不存在,经过2个1的情况存在。
那么对于每一个位置的bitset,我们首先将它上方和左边的bitset或运算一下,得到一个并集。然后再根据a[i][j]的值进行处理
当a[i][j]==0,说明当前点为0,直接取上面左边两个的或运算结果即可。
否则说明当前点为1,那么所有的情况都应该+1,也就是在bitset中所有位都左移一位,并且第0位为0(因为当前位为1,不可能有1的数量为0的情况)
那上面这些操作正好可以用(f[i-1][j]|f[i][j-1])<<a[i][j]来表示(在赋值阶段把a[i][j]的-1全部赋值为0)
最后我们查询f[n][m][(n+m-1)/2]是否存在即可。
#include<bits/stdc++.h>
#define endl "\n"
#define int long long
using namespace std;
int t, n, m, a[1001][1001];
bitset<1001> f[1001][1001];
void youlinaixu()
{
cin>>n>>m;
for(int i=1;i<=n;i++)
{
for(int j=1;j<=m;j++)
{
cin>>a[i][j];
f[i][j].reset();
if(a[i][j]==-1) a[i][j]=0;
}
}
if((n+m-1)%2!=0)
{
cout<<"NO"<<endl;
return;
}
int x=(n+m-1)/2;
f[0][1][0]=f[1][0][0]=1;
for(int i=1;i<=n;i++)
{
for(int j=1;j<=m;j++)
{
f[i][j]=(f[i][j-1]|f[i-1][j])<<a[i][j];
}
}
if(f[n][m][x]) cout<<"YES"<<endl;
else cout<<"NO"<<endl;
}
signed main()
{
ios::sync_with_stdio(0),cin.tie(0),cout.tie(0);
cin>>t;
while (t --)
{
youlinaixu();
}
}
当然这题是有正解的,我们统计路径中1的数量的最大值和最小值,假如最大值>=(n+m-1)/2并且最小值<=(n+m-1)/2,那么我们一定可以通过不断调整路径,每次令1的数量变化2来最终达到正好等于(n+m-1)/2的情况。求1的数量最大值和最小值的转移方程较为基础,在此不做展示
P10914 [蓝桥杯 2024 国 B] 跳石头

简单来说就是把集合合并然后看最大的区间,然后bitset的或运算可以相当简便的进行集合合并
不过有个问题是他只能从前向后跳不能从后向前跳,因此我们枚举时需要倒着枚举,每次令当前位置答案或上后面的答案
#include<bits/stdc++.h>
#define endl "\n"
//#define int long long
using namespace std;
const int maxn=4e4+10;
bitset<maxn> st[maxn];
int a[maxn];
void youlinaixu()
{
int n;
cin>>n;
for(int i=1;i<=n;i++)
{
cin>>a[i];
st[i][a[i]]=1;
}
for(int i=n;i>=1;i--)
{
int t1=a[i]+i,t2=i*2;
if(t1<=n)
{
st[i]|=st[t1];
}
if(t2<=n)
{
st[i]|=st[t2];
}
}
int ans=0;
for(int i=1;i<=n;i++)
{
ans=max(ans,(int)st[i].count());
}
cout<<ans;
}
signed main()
{
ios::sync_with_stdio(0),cin.tie(0),cout.tie(0);
int t=1;
//cin>>t;
while(t--)
{
youlinaixu();
}
return 0;
}

浙公网安备 33010602011771号