前缀和练习题 题解

牛客竞赛_ACM/NOI/CSP/CCPC/ICPC算法编程高难度练习赛_牛客竞赛OJ (nowcoder.com)

B 智乃酱的子集与超集

二进制状态压缩

1011 表示选取 1,2,4 号物品的状态

先求出每种状态对应的价值(即集合内所有数的异或和)

下面的思路比较难想:

枚举每个物品,枚举每种状态,对于某状态如果存在这个物品,那么这个状态去掉当前物品一定是它的子集,son[Case] (即所有子集的价值和) 累加该子集的son[Case'] ,如果不存在这个物品,那当前状态加上这个物品就一定是它的超集,chao[Case] (即所有超集的价值和) 累加该超级的chao[Case']

用这种策略可以不重复的累加每个状态的所有子集与超集的价值(可以好好思考一下)

启发

如果题目中出现集合,子集等概念,可考虑二进制状态压缩,并考虑状态之间的关联、关系。

Code

#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
ll R(){ll a=0,b=1;char c=getchar();while(c<'0'||c>'9'){if(c=='-')b=-1;c=getchar();}while(c>='0'&&c<='9'){a=a*10+c-'0';c=getchar();}return a*b;}
const ll Ma=1<<20;
ll n,m;
ll a[25];
ll son[Ma],chao[Ma];
int main(){
// 	freopen("R.txt","r",stdin);
	n=R();m=R();
	for(ll i=0;i<n;++i){
		a[i]=R();
	}
	ll Max_case=(1<<n)-1;
	for(ll i=0;i<=Max_case;++i){
		ll sum=0;
		for(ll j=0;j<n;++j){
			if((1<<j)&i) sum^=a[j];
		}
		son[i]=chao[i]=sum;
	}
	for(ll i=0;i<n;++i){
		for(ll j=0;j<=Max_case;++j){
			if(j&(1<<i)) son[j]+=son[j^(1<<i)];
			else chao[j]+=chao[j^(1<<i)];
		}
	}
	while(m--){
		ll cnt=R(),Case=0;
		while(cnt--){
			ll t=R();
			Case |= (1<<(t-1));
		}
		printf("%lld %lld\n",son[Case],chao[Case]);
	}
	return 0;
}

F 牛牛的猜球游戏

考虑前缀和的思想,消去L-1次操作以前的影响

先预处理出每一次操作后的状态

然后对第L-1次操作后的状态进行1~N重编码

那么第R次操作后的结果就是当前状态的编码

 

Code

#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
int R(){int a=0,b=1;char c=getchar();while(c<'0'||c>'9'){if(c=='-')b=-1;c=getchar();}while(c>='0'&&c<='9'){a=a*10+c-'0';c=getchar();}return a*b;}
int n,m;
int f[100010][10];
int t[10];
int main(){
// 	freopen("R.txt","r",stdin);
	n=R();m=R();
	for(int i=0;i<10;++i){
		f[0][i]=i;
	}
	for(int i=1;i<=n;++i){
		for(int j=0;j<10;++j){
			f[i][j]=f[i-1][j];
		}
		int a=R(),b=R();
		swap(f[i][a],f[i][b]);
	}
	while(m--){
		int l=R(),r=R();
		for(int i=0;i<10;++i){
			t[f[l-1][i]]=i;
		}
		for(int i=0;i<10;++i){
			printf("%d ",t[f[r][i]]);
		}
		puts("");
	}
	return 0;
}

H 小w的糖果

操作1:一次前缀和

操作2:两次前缀和

操作3:三次前缀和之后,ans[i]=sum[i-1]+sum[i]

Code

#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
int R(){int a=0,b=1;char c=getchar();while(c<'0'||c>'9'){if(c=='-')b=-1;c=getchar();}while(c>='0'&&c<='9'){a=a*10+c-'0';c=getchar();}return a*b;}
const int mod=1e9+7;
int sum[3][100010];
int n,m;
void init(){
	memset(sum,0,sizeof sum);
}
inline int M(int x){
	return x%mod;
}
int main(){
//	freopen("R.txt","r",stdin);
	int T=R();
	while(T--){
		init();
		n=R();m=R();
		while(m--){
			int type=R(),pos=R();
			sum[type-1][pos]++;
		}
		for(int i=1;i<=n;++i){
			for(int j=0;j<3;++j){
				sum[j][i]+=sum[j][i-1];
				sum[j][i]%=mod;
			}
		}
		for(int i=1;i<=n;++i){
			sum[1][i]+=sum[1][i-1];
			sum[2][i]+=sum[2][i-1];
			sum[1][i]%=mod;
			sum[2][i]%=mod;
		}
		for(int i=1;i<=n;++i){
			sum[2][i]+=sum[2][i-1];
			sum[2][i]%=mod;
			printf("%d ",M(M(M(sum[0][i]+sum[1][i])+sum[2][i])+sum[2][i-1]));
		}
		putchar('\n');
	}
	return 0;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值