【补题日志2022.11.5】 Codeforces Round #832 (Div. 2)

Problem A:Two Groups

A

导言:这道题的意思是让我们八所有的数分为两个集合,且集合的绝对值之差最大,两个集合可以为空。

其实就是吓人罢了,事实上我们假定任意分两个集合,那么s2的绝对值要么0,要么非零。

如果绝对值是零的话,那么我们可以直接把s2移到s1里面,差值不变。

如果绝对值不是零的话,我们也把s2移到s1里面减数变小比如x,那么s1的值要么减小相同的量,要么减小较小的量,要么增加相同的量,肯定是大于等于原来的值。

所以只要求和就可以了。

注意 long long [悲];

#include <bits/stdc++.h>
using namespace std;
const int N = 1e5 + 5;
int a[N];
int main() {
	ios::sync_with_stdio(false);
	cin.tie(0);
	int t;
	cin >> t;
	while (t--) {
		int n;
		cin >> n;
		long long sum = 0;
		for (int i = 0; i < n; i++) {
			cin >> a[i];
			sum += a[i];
		}
		cout << abs(sum) << '\n';
	}
	return 0;
}

Problem B:BANBAN

B

导言:

这道博主一开始看错了,以为是不存在连续的字符串BAN但是,实际上,不是这个意思,他的意思是不存在BAN的公共子串。

A string a is a subsequence of a string b if a can be obtained from b by deletion of several (possibly, zero or all) characters.

一个字符串是另一个字符串的子串,如果这个字串可以通过删除部分的字符转换成另一个字符串。

悲剧从这里开始。

它的意思是要我们转换成BNN(NNN)BNNBNN....(BAB)BAABAABAA;

注意循环周期是3,博主写成2,枯了。下面是AC代码

#include <bits/stdc++.h>
using namespace std;
const int N = 1e5 + 5;
int a[N];
int main() {
	ios::sync_with_stdio(false);
	cin.tie(0);
	int t;
	cin >> t;
		while (t--) {
		int n;
		cin >> n;
		if (n == 1) {
			cout << 1 << '\n';
			cout << 1 << ' ' << 2 << '\n';
		} else if (n == 2) {
			cout << 1 << '\n';
			cout << 2 << ' ' << 6 << '\n';//抄题目
		} else {
			if (n % 2 == 0) {
				cout << n / 2 << '\n';
				for (int i = 1; i <= (n / 2); i++) {
						cout << 3 * i - 1 << ' ' << 3 * n - 3 * (i - 1) << '\n'; 
				}//A的位置是2 5 8 ………… N的位置是3n 3n - 3 3n - 6
				cout << '\n';
			} else {
				cout << n / 2 + 1 << '\n';
				for (int i = 1; i <= (n / 2); i++) {
						cout << 3 * i - 1 << ' ' << 3 * n - 3 * (i - 1) << '\n';
				}
				cout << 3 * (n + 1) / 2  << ' ' << 1 << '\n';//中间的BAN把N和第一个B交换
			}
		}
	}
	return 0;
}

下面是大佬的代码

#include <bits/stdc++.h>
using namespace std; 
void solve() {
	int n;
	scanf("%d", &n);
	int m = (n + 1) / 2;//m的次数其实是不必分奇偶的,这样可以直接向上取整
	printf("%d\n", m);
	for (int i = 0; i < m; i++) {
		printf("%d %d\n", 3 * i + 1, 3 * (n - i));
//BANBANBAN m = 2
//1 9 NANBANBAB
//4 6 NANNABBAB
//思路比博主清楚多了
	}
}
 
int main() {
	int t;
	scanf("%d", &t);
	while(t--) solve();
	return 0; 
}

 Problem C :swap game

C

导言:

如果说第一个不是最小值,

试一下:

5 4 4

A:4 4 4

B:4 3 4

A:4 3 3

B:3 3 3

A:3 2 3

B:3 2 2

A: 2 2 2

B:2 1 2

A:1 1 2

B:1 0 2

A: 0 0 2

B负

最小值是一个烫手的山芋,最小值是第一个为0的数。

如果说第一个是最小值,那么alice不得不减掉最小值,然后bob会把它再拉回来,在这个过程中最小值一直在减小,而bob一直都没有直接接触(操作的时候开第一个位置)到最小值,所以经过若干次操作后,肯定是Alice拿到0;

如果说第一个不是最小值,那么alice肯定会操作一次把最小值放在第一个位置,从而逼迫bob一步步减小最小值,而自己就可以一直不用减最小值,而是减其他的值来控制住最小值在bob操作的时候一定在第一个位置。

大佬代码:

#include <bits/stdc++.h>
using namespace std;
int main() {
	ios_base::sync_with_stdio(0);
	cin.tie(0);
	int t;
	cin >> t;
	while (t--) {
		int n;
		cin >> n;
		vector<int> a(n);//定义一个有n个元素的vector数组
		for (int i = 0; i < n; i++) {
			cin >> a[i];
		}//min_element(首地址,尾地址)返回的是迭代器,是地址,加*返回值
		if (a[0] == *min_element(a.begin(), a.end())) {//如果a[0]是最小值
			cout << "Bob" << '\n';
		} else {
			cout << "Alice" << '\n';
		}
	}
}

Problem D:Yet another problem

 D

导言:

问讯每个区间内是否能够全部置零, 如果可以的话就输出最小的操作数,否则输出-1.

解读大佬代码:

#include<bits/stdc++.h>
using namespace std;
const int mxn=8e5+5;
unordered_map<int,int>nxt;
int n,q;
#define ll long long
ll sm[mxn],xm[mxn],a[mxn];
int wee[mxn];//wee数组的含义是以某个点为起点的最长的异或和为零的区间的末端
int main(){
	ios_base::sync_with_stdio(false);
	cin.tie(0),cout.tie(0);
	cin>>n>>q;
	for(int i=1;i<=n;++i)cin>>a[i],sm[i]=sm[i-1]+a[i],xm[i]=xm[i-1]^a[i];
/*sm数组存储的是从1到i的数的和,xm存储的是1到i的异或和*/
	for(int i=1;i<=n+1;++i)wee[i]=n+1;//初始化wee数组是它本身
	for(int i=n;~i;--i){//减到-1为止
		if(nxt[xm[i]]!=0){//如果unmap里面有值的话,说明该异或和曾经出现过一次,异或和与xm[i]相同*/
/*注意,前i的异或等于前nxt[xm[i]]异或和说明,i + 1 到nxt[xm[i]]项的异或和为0*/
			if((nxt[xm[i]]-i)%2==0)wee[i+1]=wee[nxt[xm[i]]+1];/*查看这个区间是否是奇数区间,如果不是的话,以i + 1为起点的区间到以nxt[xm[i] + 1]为起点的区间的末端的区间,一个奇数区间并上一个偶数区间仍然是一个奇数的区间*/
			else wee[i+1]=nxt[xm[i]];
/*如果是奇数区间的话,连接上一个节点*/
		}//这里是似乎是在找到奇数的区间
		nxt[xm[i]]=i;/*nxt存储的是异或和和i的映射,异或和相同的取最小的i,意味着某个区间异或和为0,结合wee数组存储的节点,找最小的2个长度的区间*/
	}
	for(;q--;){
		int l,r;cin>>l>>r;
		if(sm[r]==sm[l-1])cout<<"0\n";//如果区间内的和为0,说明都是0
		else if(xm[r]!=xm[l-1])cout<<"-1\n";//如果整个区间内的异或和不为零,不可能
/*如果有异或和不为零的话,肯定有一个数是找不到配对的*/
		else if((r-l+1)%2==1)cout<<"1\n";//如果区间长度奇数,只要一次
		else{
			if(a[l]==0 or a[r]==0)cout<<"1\n";//如果两端有一个是零,相当于奇数
			else if(wee[l]<=r)cout<<"2\n";//如果最右区间在范围内的话,2次即可
			else cout<<"-1\n";//其他偶数区间不可能
		}
	}
}

大概思路是,比较两端区间异或和是否为零,其中wee数组是用来存在l和r的关系的,如果输入最左边的发现返回的,值超出范围说明找不到合适的区间,使得两段的异或和都为0;

操作都只能对奇数长度的区间进行,偶数如果要操作的话一定要操作两次,我们要从最左端分离出一段异或和为0的区间,因为总的异或和为零,那么剩余的偶数区间,加上个操作后的末端零,就变成了奇数的区间,可以操作,而这个奇数的区间的异或和必然是为零的,异或和无非是不进位加法,各个位数去掉偶数个数仍然是偶数,必定为零。如果找不到拿就一定不行。

只不过这个记录区间的线性数组wee有点抽象。

Problem E:

E

导言:

两个数列加起来的的新数列是严格递增的,而且两个数列是单调不减的。

感觉有种自然数拆分的感觉,但是这里的数据量巨大。

大佬代码:

还是太菜了,看不懂。

#include <bits/stdc++.h>
using namespace std;
#define int long long//。。。 
const int p=1e9+7;//最大数 
int po(int a,int b) {//po???反正看起来像是求乘法逆元的 
	if(b==0) return 1;
	if(b==1) return a;
	if(b%2==0) {
		int u=po(a,b/2);
		return (u*u)%p;
	} else {
		int u=po(a,b-1);
		return (a*u)%p;
	}
}
int inv(int x) {//乘法逆元 
	return po(x,p-2);
}
const int maxn=1e7+5;
int fact[maxn];//阶乘的模数组 
int invf[maxn];//乘法逆元模数组 
int po2[maxn];
int c(int x,int y,int z) {//??? 
	if(x<0 || y<0 || z<0) return 0;
	int ans=fact[x+y+z];
	ans*=invf[x];
	ans%=p;
	ans*=invf[y];
	ans%=p;
	ans*=invf[z];
	ans%=p;
	return ans;
}
int f(int n,int m) {
	int ans=0;
	for(int k=max(n,m); k<=n+m; ++k) {
		int k1=k-m;
		int k2=k-n;
		int k3=k-k1-k2;
		int h=((k+1)*c(k1,k2,k3));//??? 
		h%=p;
		int o=po2[k];
		if(k3%2==0) {
			ans+=(o*h);
			ans%=p;
		} else {
			ans-=(o*h);
			ans%=p;
		}
	}
	return (ans%p+p)%p;
}
int32_t main() {
	ios_base::sync_with_stdio(false);
	cin.tie(0);
	cout.tie(0);
	fact[0]=1;
	for(int i=1; i<maxn; ++i) {
		fact[i]=(fact[i-1]*i)%p;//求阶乘的模 
	}
	invf[maxn-1]=inv(fact[maxn-1]);
	for(int i=maxn-2; i>=0; --i) {
		invf[i]=(invf[i+1]*(i+1))%p;//填充乘法逆元 
	}
	assert(invf[0]==1);//断言 ??? 
	po2[0]=1;
	for(int i=1; i<maxn; ++i) {
		po2[i]=po2[i-1]*2;//??? 
		if(po2[i]>=p) po2[i]-=p;
	}
	int t;
	cin>>t;
	while(t--) {
		int n,m;
		cin>>n>>m;//??? 
		int ans=f(n,m)-2*f(n-1,m)-2*f(n,m-1)+f(n-2,m)+f(n,m-2)+4*f(n-1,m-1)+f(n-2,m-2)-2*f(n-1,m-2)-2*f(n-2,m-1);
		cout<<(ans%p+p)%p<<'\n';
	}
	return 0;
}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值