Bitset

bitset 在#include< bitset >中,用于存储 0/1 的容器

bitset的库函数

可支持左移右移等操作
set():每一位赋为 1
reset():每一位赋为 0
count():统计1的个数
size():大小
还有其它的,常用的就以上

用处

在某些情况下通过 bitset 可以优化程序的运行效率
O ( n w ) O(\frac{n}{w}) O(wn)

例题

T1

贪心只能过样例

这题可以用 dp 做,转移方程很简单:

f i , j = ∨ k = a b f i − 1 , j − k 2 f_{i,j}=\vee_{k=a}^b f_{i-1,j-k^2} fi,j=k=abfi1,jk2
然后或运算可以用bitset优化

#include <iostream>
#include <cstdio>
#include <bitset>
using namespace std;
const int N = 101;
int n, a[N], b[N];
bitset<N *N *N> f[N];
int main() {
    cin >> n;

    for (int i = 1; i <= n; ++i)
        cin >> a[i] >> b[i];
    f[0][0] = 1;
    for (int i = 1; i <= n; ++i) 
        for (int j = a[i]; j <= b[i]; ++j) 
            f[i] |= (f[i - 1] << (j * j));       
    cout << f[n].count();
}
T2

Two Arrays

先按 w i w_i wi排序
先考虑暴力枚举一个i,对每一个i去找到合法的最小的j即可,是 O ( n 2 m ) O(n^2m) O(n2m)
若对每个 a i a_i ai开个bitset就好了,是 O ( n 2 m w ) O(\frac{n^2m}w) O(wn2m)
注意优化空间

#include<bits/stdc++.h>
#define pb push_back 
using namespace std;
const int N=1e5+10;
const int M=5e5+10;
int n,m,lim,v[M],len,cnt[M],ans=2e9+10,idx[M],tot;
vector<int>buc[M];
struct st{
	int a[6],v;
	bool operator <(const st &T)const{
		return v<T.v;
	}
}t[N];
bitset<N>s[800];
int main(){
	cin>>n>>m;lim=sqrt(n*m);
	for(int i=1;i<=n;i++){
		for(int j=1;j<=m;j++)
			scanf("%d",&t[i].a[j]),v[++len]=t[i].a[j];
		scanf("%d",&t[i].v);
	}
	sort(t+1,t+1+n);
	sort(v+1,v+1+len);
	len=unique(v+1,v+1+len)-v-1;
	for(int i=1;i<=n;i++)
		for(int j=1;j<=m;j++)
			buc[t[i].a[j]=lower_bound(v+1,v+1+len,t[i].a[j])-v].pb(i),cnt[t[i].a[j]]++;
	for(int i=1;i<=len;i++)
		if(cnt[i]>=lim){
			idx[i]=++tot;
			for(int x:buc[i])s[tot].set(x);
		}
	bitset<N>tmp;
	for(int i=1;i<=n;i++){
		tmp.reset();
		for(int j=1;j<=m;j++)
			if(idx[t[i].a[j]]) tmp|=s[idx[t[i].a[j]]];
		tmp.flip();
		for(int j=1;j<=m;j++)
			if(!idx[t[i].a[j]])
				for(int x:buc[t[i].a[j]])tmp.reset(x);
		int pos=tmp._Find_next(0);
		if(pos<=n) ans=min(ans,t[i].v+t[pos].v);
	}
	printf("%d\n",ans==2e9+10?-1:ans);
}
T3 退役的你

CQYZ出的题,比较良心,可惜没做

给定大小为 n n n 的整数集合 S S S ,求一个大小为 K K K 的整数集合 T T T, 使 0 < T i < 2 30 0<T_i<2^{30} 0<Ti<230,最大化
∑ x ∈ S ∑ y ∈ T [ x & y = y ] \sum_{x\in S}\sum_{y\in T} [x\& y=y] xSyT[x&y=y]

贪心地在优先队列中先放 2 30 − 1 2^{30}-1 2301,每次选择其中一个数 x x x加入 T T T
,然后考虑将 x x x某一位上的一变为零得到 x ′ x' x ,计算 x ′ x' x 的贡献,并把 加入 x ′ x' x待选集合,就行了,然后计算 x ′ x' x 的贡献可以bitset优化

#include<bits/stdc++.h>
using namespace std;
const int N = 5e5+7, M = 1e3+7, P = 1e9+7;
int n, K, S[N], ans[M];
priority_queue<pair<int, int> > pq;
bitset<N> bit[30],bt[M];
unordered_map<int, int> vis;
int main(){
	freopen("afo.in", "r", stdin);
	freopen("afo.out", "w", stdout);
	scanf("%d%d",&n,&K);
	for(int i = 1; i <= n; i++) {
		scanf("%d", &S[i]);
		for(int j = 0; j < 30; j++) 
			if(S[i] & (1 << j)) bit[j][i] = 1;
	}
	pq.push(make_pair(n, (1 << 30) - 1));
	for(int T = 1; T <= K; T++) {
		int x = pq.top().second;
		ans[T] = x;
		pq.pop();
		printf("%d ", x);
		if(T > 1) 
			bt[T] = bt[vis[x]] | bit[__builtin_ctz(x ^ ans[vis[x]])];
		for(int i = 0; i < 30; i++) if((x & (1 << i)) && !vis.count(x ^ (1 << i))){
			vis[x ^ (1 << i)] = T;
			bitset<N> tmp = bt[T] | bit[i];
			pq.push(make_pair(n - tmp.count(), x ^ (1 << i)));
		}
	}
 }
T4 Substrings in a String

题目

考虑暴力
对于每个2询问, O ( ∣ s ∣ ∣ y ∣ ) O(|s||y|) O(s∣∣y)暴力统计答案,因为 ∑ ∣ y ∣ < = 1 e 5 \sum|y|<=1e5 y<=1e5
所以为 O ( n 2 ) O(n^2) O(n2)
用bitset优化匹配的过程后为 O ( n 2 w ) O(\frac{n^2}w) O(wn2),吊打标算

#include<bits/stdc++.h>
using namespace std;
const int N=1e5+7;
int q;
bitset<N> mp[26],ans;
char s[N],c[N];
int main(){
    scanf("%s%d",s+1,&q);
    int n=strlen(s+1);
    for(int i=1;i<=n;i++)mp[s[i]-'a'][i]=1;
    while(q--){
        int op,x,y;
        scanf("%d",&op);
        if(op==1){
            scanf("%d%s",&x,c);
            mp[s[x]-'a'][x]=0;mp[c[0]-'a'][x]=1;
            s[x]=c[0];
        }
        else {
            scanf("%d%d%s",&x,&y,c);
            int len=strlen(c);
            ans.set();
            for(int i=0;i<len;i++)ans&=(mp[c[i]-'a']>>i);
            printf("%d\n",max(0,(int)(ans>>x).count()-(int)(ans>>(y-len+2)).count()));
        }
    }
} 

就这样

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值