Problem A:Two Groups
导言:这道题的意思是让我们八所有的数分为两个集合,且集合的绝对值之差最大,两个集合可以为空。
其实就是吓人罢了,事实上我们假定任意分两个集合,那么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
导言:
这道博主一开始看错了,以为是不存在连续的字符串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
导言:
如果说第一个不是最小值,
试一下:
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
导言:
问讯每个区间内是否能够全部置零, 如果可以的话就输出最小的操作数,否则输出-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:
导言:
两个数列加起来的的新数列是严格递增的,而且两个数列是单调不减的。
感觉有种自然数拆分的感觉,但是这里的数据量巨大。
大佬代码:
还是太菜了,看不懂。
#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;
}