Codeforces Round #696
文章目录
- CF1474A Puzzle From the Future
- 题意:
- 题解:
- 代码:
- CF1474B Different Divisors
- 题意:
- 题解:
- 代码:
- CF1474C Array Destruction
- 题意:
- 题解:
- 代码:
- CF1474D Cleaning
- 题意:
- 题解:
- 代码:
- CF1474E What Is It?
- 题意:
- 题解:
- 代码:
- CF1474F 1 2 3 4 ..
- 题意:
- 题解:
- 代码:
CF1474A Puzzle From the Future
题意:
在2022年,Mike发现了两个长度为n的二进制整数a和b(它们都只由前导有0的数字(和1)表示)。为了不忘记它们,他想用以下方式构造整数d:
他将a和b按位加和而不进行进位转换,从而得到一个整数c,所以c可能有一个或多个2。例如,0110和1101按位求和的结果是1211,011000和011000按位加和的结果是022000;
之后,Mike将c中相等的连续数字折叠为1位,因此得到d。经过这个操作,1211变成121,022000变成020(所以,d不会有相等的连续数字)。
不幸的是,迈克在自己计算d之前就失去了整数a。现在,为了让他高兴起来,你要找到一个长度为n的二进制整数a,使d成为最大的可能的整数。
最大可能的整数的意思是102>21,012<101,021=21,以此类推。
题解:
规律题,找到规律就行
我们发现a的第一位肯定是1(从左往右数),为了让d更大,我们要让a加b更大,且不能存在一样的数,所以从第二位开始,如果a和b的第i-1位 等于 b的第i位+1,说明第a位就不能取1,只能取0,反之可取1
代码:
#include<bits/stdc++.h>
typedef long long ll;
using namespace std;
inline int read(){int s=0,w=1;char ch=getchar();while(ch<'0'||ch>'9'){if(ch=='-')w=-1;ch=getchar();}while(ch>='0'&&ch<='9') s=s*10+ch-'0',ch=getchar();//s=(s<<3)+(s<<1)+(ch^48);return s*w;
}
int main()
{int t;cin>>t;while(t--){int n;cin>>n;string s;cin>>s;string b="1";for(int i=1;i<n;i++){int w=s[i]-'0';int x,y;x=s[i-1]-'0';y=b[i-1]-'0';if(w+1==x+y)b+='0';else b+='1';}cout<<b<<endl;}return 0;
}
CF1474B Different Divisors
题意:
求一个数,这个数至少有4个除数,且每个除数的差至少是d。
题解:
根据题意多举例子就能发现:
第一位肯定是1,最后一位肯定是中间所有数的乘积,然后第二位比第1位大d,且我们要保证除了第一位和最后一位,其他位的数都必须是质数,这样可以保证最后一位最小
所以我们每次找大于前一位+d的质数
代码:
#include<bits/stdc++.h>
typedef long long ll;
using namespace std;
inline int read(){int s=0,w=1;char ch=getchar();while(ch<'0'||ch>'9'){if(ch=='-')w=-1;ch=getchar();}while(ch>='0'&&ch<='9') s=s*10+ch-'0',ch=getchar();//s=(s<<3)+(s<<1)+(ch^48);return s*w;
}
const int maxn=1e7+9;
int tag[maxn],prime[maxn];
int cnt=0;
void Prime(int N)
{memset(tag,0,sizeof(tag));tag[0]=tag[1]=1;for(int i=2;i<N;i++){if(!tag[i])prime[cnt++]=i;for(int j=0;j<cnt&&prime[j]*i<N;j++){tag[i*prime[j]]=1;if(i%prime[j]==0)break;}}
}
int main()
{Prime(1000000);int t;cin>>t;while(t--){int d;cin>>d;int x=1;x+=d;x=lower_bound(prime,prime+cnt,x)-prime;int y=prime[x];y+=d;y=lower_bound(prime,prime+cnt,y)-prime;cout<<prime[x]*prime[y]<<endl;}
}
CF1474C Array Destruction
题意:
题目给出一个长度为2n的正整数序列,现在问你是否存在一个x使得可以不断的进行如下操作,直到这个序列变为空:
从序列中找到两个数字a1,a2,使得a1+a2==x,然后从序列中删掉这两个数字,x的值也被更新,x=max(a1,a+2)。
题解:
因为n的范围并不大,所以x可以暴力枚举
因为x是单调递增的,所以第一个x必然是最大的那个数加上另一个数,最大的数是确定的,另一个数可以枚举
所以一开始枚举2n-1个另一个数,和最大的那个数一起组成最初的x,然后接下来每一步都知道x是谁,且知道最大值是谁,那么x-最大值直接二分查找。一直进行下去,如果中间有二分找不到的数,就从头枚举另一个数重新来
复杂度:O(n2logn)
这题主要看你set用的熟不熟练了
代码:
#include <bits/stdc++.h>
using namespace std;
const int maxn = 2020;
int T, n, a[maxn], ans;
multiset<int> st;
vector<pair<int, int> > vec;bool _check(int ii) {st.clear();for (int i = 0; i < 2*n-1; i ++) {if (i == ii) continue;st.insert(a[i]);}vec.clear();vec.push_back({a[2*n-1], a[ii]});ans = a[2*n-1] + a[ii];int tmp = a[2*n-1];for (int i = 1; i < n; i ++) {multiset<int>::iterator it = st.end(), it2;it --;int x = *it;st.erase(it);it2 = st.lower_bound(tmp - x);if (it2 == st.end()) return false;int y = *it2;if (x + y != tmp) return false;vec.push_back({x, y});tmp = max(x, y);st.erase(it2);}return true;
}bool check() {for (int i = 0; i < 2*n-1; i ++) if (_check(i)) return true;return false;
}int main() {scanf("%d", &T);while (T --) {scanf("%d", &n);for (int i = 0; i < 2*n; i ++) scanf("%d", a+i);sort(a, a+2*n);if (check()) {puts("YES");printf("%d\n", ans);for (auto x : vec) printf("%d %d\n", x.first, x.second);}else puts("NO");}return 0;
}
CF1474D Cleaning
题意:
每次拿走两个相邻位置的一个石头(可以拿多次),且有且只有一次交换两个相邻位置的机会,问能不能将所有石头拿走
题解:
如果有后一个数字大于前一个数字,我们从前往后就会出现后一个为0,前一个还不为0的情况,所以一定不可能。同理从后往前删除是一样的情况
我们用pre预先处理从前往后的差分
suf预处理从后往前的差分
如果pre与suf数组存在同一位置相等,说明就可以全部拿走
因为还有一次交换的机会,所以直接暴力交换i和i+1两个位置,然后求出当前位置新的pre和和suf,用于判断是否相等
代码:
#include<iostream>
#include<algorithm>
#include<cmath>
#include<cstring>
#include<cstdio>
using namespace std ;
const int maxn = 2e5 + 10 ;
typedef long long ll ;
ll a[maxn] , pre[maxn] , suf[maxn] ;
const ll inf = 1e18 ;int main(){int t ;cin >> t ;while(t--){ll n ;cin >> n ;memset(pre , 0 , sizeof(pre)) ;memset(suf , 0 , sizeof(suf)) ;for(ll i = 1 ; i <= n ; i++) cin >> a[i] ;for(ll i = 1 ; i <= n ; i++){if(a[i] >= pre[i-1]) pre[i] = a[i] - pre[i-1] ;else pre[i] = inf / 2 ; // 预处理pre,当有不符合情况的时候,pre数组从此往后全为inf/2}for(ll i = n ; i >= 1 ; i--){if(a[i] >= suf[i+1]) suf[i] = a[i] - suf[i+1] ;else suf[i] = inf ;// 从后往前,如果有不符合,那么全为inf}bool flag = 1 ;for(ll i = 1 ; i <= n - 1 ; i++){if(pre[i] == suf[i+1]){ // 如果可以接上的话直接输出yes就行flag = 0 ;break ;}else{ll tmp1 = a[i] ; // 如果接不上,那么交换a[i]与a[i+1] 然后实现pre[i] 和 suf[i+1] 如果接的上输出yesll tmp2 = a[i+1] ;if(tmp1 >= suf[i+2] && tmp2 >= pre[i-1]){tmp1 -= suf[i+2] ;tmp2 -= pre[i-1] ;if(tmp1 == tmp2){flag = 0 ;break ;}}}}if(!flag) cout << "YES" << endl ;else cout << "NO" << endl ;}return 0 ;
}
CF1474E What Is It?
题意:
一个排列,你可以选择i , j ( i ≠ j ) ,满足a [ j ] = i , 然后交换a[i],a[j],交换收益为 (i-j)2。
让你构造一个长度为n的排列,使得交换过程所得收益最大。
题解:
我们不难发现我们每次交换,都会使得至少一个数归位(也就是a[i]=i),当所有位置归位时我们将无法再交换,所以最多交换n-1次
我们再考虑距离为n-1的对数最多是多少?只有一对,就是(1,n),除此之外没有了
我们再考虑n-2的对数最多是多少?最多是两对,就是(1,n-1)和(2,n),(当然其他考虑也是一样的),最多只有两对
当距离为n-3的也只有两对
那么最大收益就是(n-1)2 + 2*(n-2)2 + 2*(n-3)2+…
这样收益就算出来了
至于步骤我们可以你想考虑,也就是一开始数组a都是归位的,然后先弄(n-1)对,再弄(n-2)对,这样就得到最后的排列和交换过程
代码:
#include <cstdio>
#include <cstring>
#include <algorithm>
#include <vector>
#include <map>using namespace std;typedef long long ll;const int INF = 0x3f3f3f3f;
const int maxn = 2e5 + 7;
int a[maxn];
int main() {int T;scanf("%d",&T);while(T--) {int n;scanf("%d",&n);for(int i = 1;i <= n;i++) {a[i] = i;}vector<pair<int,int> >vec;ll ans = 0;int cnt = n - 1;for(int i = n - 1;;i--) { //交换两点的距离swap(a[i + 1],a[1]);vec.push_back({i + 1,1});ans += 1ll * i * i;cnt--;if(cnt == 0) break;if(i == n - 1) continue;swap(a[n - i],a[n]);vec.push_back({n - i,n});ans += 1ll * i * i;cnt--;if(cnt == 0) break;}printf("%lld\n",ans);for(int i = 1;i <= n;i++) {printf("%d ",a[i]);}printf("\n");printf("%d\n",n - 1);for(int i = vec.size() - 1;i >= 0;i--) {printf("%d %d\n",vec[i].first,vec[i].second);}}return 0;
}