A. Maximum GCD
题意:给定一个n,在1-n中任选两个数,求最大的gcd
思路:显然要让gcd最大,就是要让能约掉的因子最小。所以答案就是n/2,因为gcd(n/2,n/2*2)必然是最大的。
AC代码:
#include <bits/stdc++.h>
#define int long long
const int N = 4e5+10;
const int mod = 1e9+7;
using namespace std;
int n,m;
string s,ss;
int a[N],b[N];
signed main(){
int t;
cin>>t;
while(t--){
cin>>n;
if(n<=3){
cout<<1<<endl;
}else{
cout<<n/2<<endl;
}
}
return 0;
}
B. GCD Compression
题意:给定2n个数,现在要进行操作,任选两个数,把他们的和放到另一个数组中。另一个数组的大小是n-1,所以在此之前,还要先删掉a中的两个数。 最后要使得 b 数组所有元素的 gcd 大于 1。
思路:gcd要大于1,那自然想到就简单的结果就是gcd >= 2,也就是保证b数组全是偶数就完了。奇数+奇数 = 偶数。 偶数+偶数 = 偶数。那么只要偶数的个数和奇数的个都是偶数个就好了。所以统计奇偶元素的个数就行了。 如果都是奇数呢,同时删掉一个就好了,和上次的题一样的套路。
AC代码:
#include <bits/stdc++.h>
#define int long long
const int N = 4e5+10;
const int mod = 1e9+7;
using namespace std;
int n,m;
string s,ss;
int a[N],b[N];
vector<int> v1;
vector<int> v2;
signed main(){
int t;
cin>>t;
while(t--){
cin>>n;
v1.clear();
v2.clear();
int cnt = 0;
for(int i = 0 ; i < 2*n ; i ++){
cin>>a[i];
cnt += a[i]%2;
if(a[i]%2) v1.push_back(i+1);
else v2.push_back(i+1);
}
if(cnt%2 == 0){
if(cnt > 0) v1.pop_back(),v1.pop_back();
else v2.pop_back(),v2.pop_back();
}else{
v1.pop_back(),v2.pop_back();
}
for(int i = 0 ; i < v1.size() ; i += 2){
cout<<v1[i]<<" "<<v1[i+1]<<endl;
}
for(int i = 0 ; i < v2.size() ; i += 2){
cout<<v2[i]<<" "<<v2[i+1]<<endl;
}
}
return 0;
}
C. Number Game
题意:给定一个数n,只能进行两种操作,n 除以一个 奇数因子(包括自身),n 减掉 1,当n等于1时,游戏结束。
思路:显然 n = 1,的时候,后手胜(因为先手已经不能操作了)。n = 2,的时候先手胜,n = 3的时候,还是先手胜利,且n为奇数的时候都是先手胜利,n为奇数除以自身就完事了。那么当n为偶数时就有意思了。分情况讨论:
1. 没有奇数因子,就是2的幂次。那么这时候,先手只能进行减1操作。然后就变成奇数了。此时 后手胜。
2. 有奇数因子,且 偶数因子大于4!那么先手 先拿掉这个奇数因子,就变成了偶数。后手只能减1操作,这时候又变成奇数。先手拿掉,就赢了。
3. 有奇数因子,且 偶数因子只有2,这时候判断奇数因子,是否为质数。如果为质数,那么只能进行一次操作1和一次操作2,那么后手必胜,反之如果不是质数,可以多进行一次操作1,先手必胜。如:2x3x3x7,那么先手可以拿掉 3x7,后手要么减1,要么拿掉3,但是都是输。
AC代码:
#include <bits/stdc++.h>
#define int long long
const int N = 4e5+10;
const int mod = 1e9+7;
using namespace std;
int n,m;
string s,ss;
int a[N],b[N];
vector<int> v1;
vector<int> v2;
bool prime(int x){
for(int i = 2 ; i*i <= x ; i ++){
if(x%i == 0) return false;
}
return true;
}
signed main(){
int t;
cin>>t;
while(t--){
cin>>n;
if(n == 1) cout<<"FastestFinger"<<endl;
else if(n == 2){
cout<<"Ashishgup"<<endl;
}else if(n%2){
cout<<"Ashishgup"<<endl;
}else{
int cnt = 0 ;
while(n%2 == 0){
n /= 2;
cnt ++;
}
if(n == 1){
cout<<"FastestFinger"<<endl;
}else{
if(cnt >= 2)
cout<<"Ashishgup"<<endl;
else{
if(prime(n))
cout<<"FastestFinger"<<endl;
else{
cout<<"Ashishgup"<<endl;
}
}
}
}
}
return 0;
}
D. Odd-Even Subsequence
题意:a数组有n个元素,现在要从中选出一个长度为k的序列S。并且计算序列的cost = min(max(s1,s3,s5,…),max(s2,s4,s6,…)),也就是 奇数位子上的最大值 和 偶数位置上的最大值 的 最小值。
思路:二分。直接二分答案。如果mid成立,也就是从a数组可以选出一个长度大于k的,奇数位置上全 不大于mid或者偶数位置上全不大于mid的。扫描两遍a数组,一次判断奇数位置,一次判断偶数位置,假设当前为奇数位置,那么奇数位置只能放不大于mid的数,如果是,长度=1,现在来到了偶数位置,偶数位置不关心他的大小,直接长度+1然后取下一个,然后判断选出来序列的长度是否 >= k
AC代码:
#include <bits/stdc++.h>
#define int long long
const int N = 4e5+10;
const int mod = 1e9+7;
using namespace std;
int n,m,k;
string s,ss;
int a[N],b[N],c[N];
vector<int> v1;
vector<int> v2;
bool check(int mid){
int pos1 = 0;
for(int i = 0 ; i < n ; i ++){
if(pos1%2 == 0){
if(a[i] <= mid){
pos1 ++;
}
}else{
pos1 ++;
}
}
int pos2 = 0;
for(int i = 0 ; i < n ; i ++){
if(pos2%2 == 1){
if(a[i] <= mid){
pos2 ++;
}
}else{
pos2 ++;
}
}
return (pos1 >= k || pos2 >= k);
}
signed main(){
int t = 1;
//cin>>t;
while(t--){
cin>>n>>k;
for(int i = 0 ; i < n ; i ++) cin>>a[i];
int l = 1,r = 1e9+10;
while(l < r){
int mid = (l+r)/2;
if(check(mid)){
r = mid;
}else{
l = mid+1;
}
}
cout<<l<<endl;
}
return 0;
}
E. Binary Subsequence Rotation
题意:给定一个01串,要变成目标串。可以选择任意一个子序列。然后顺时针移动一次。也就是最前面的到最后面,其实的移动到前一个元素的位置。问需要多少次。
思路:考虑贪心。首先,如果两个位置相等,那自然不用判断了。其次,选110或者001,这种也是非最优的选择,因为每次变化都有一个位置没有变,相当于浪费一次。所以显然是01交替出现。也就是只有两种序列可选 010101… 101010…,然后就遍历扫一遍,看这两种各最多需要多少个就行了。
AC代码:
#include <iostream>
#include <bits/stdc++.h>
#define int long long
#define mk make_pair
#define gcd __gcd
#define pb push_back
using namespace std;
const double eps = 1e-10;
const int mod = 1e9+7;
const int N = 1e6+7;
int n,m,k,t = 1,cas = 1;
int a[N],b[N],c[N];
int cnt1=0,cnt2=0,cnt3=0;
signed main(){
string s1,s2;
while(cin>>n){
cin>>s1>>s2;
int maxx1 = 0;
int maxx2 = 0;
int cnt0 = 0;
int cnt1 = 0;
for(int i = 0 ; i < n ; i ++){
if(s1[i] == s2[i]) continue;
if(s1[i] == '0'){
if(cnt1) cnt1--;
else cnt0++;
}else{
if(cnt0) cnt0--;
else cnt1++;
}
maxx1 = max(maxx1,abs(cnt1));
maxx2 = max(maxx2,abs(cnt0));
}
if(cnt1 || cnt0 )cout<<-1<<endl;
else cout<<maxx1+maxx2<<endl;
}
}
/**
**/
F. The Hidden Pair
题意:给定一个数。给定一个s和t,保证他们不一样。然后每次可以询问一个点集合。 会返回这个点集合中。dis(s,x)+dis(x,t)最小的点。也就是到他们的距离和最小。如果有多个,随机返回一个。并且告知dis(s,x)+dis(x,t) 这个值。问能否在 14(easy)/11(hard)次询问之内找出 s,t是哪两个点。
思路参考:https://blog.csdn.net/qq_45458915/article/details/106909780
思路:第一次询问。因为啥信息也没有。只能询问所有的点。这样就可以得到一个在 s-t 的最短路径上的某一个点,并且获得了 len = dis(s,t)。然后以这个点rt为根,dfs把到根距离相等的点放到同一堆。然后需要找出。距离根节点深度最大的并且到 dis(s,x)+dis(x,t) 距离等于 len 的点。就是这个过程。当然不可能遍历深度。 答案复杂度显然和log有关。 所以可以二分深度。 直到找到一个深度最大的,距离为len 的点。 那么这个点,必然是s或者t中的某一个。 然后以这个点为根。建树。然后再询问一次,找到深度为 len 并且距离为len 的点。就是另一个点了。easy版的 可以询问14次。 二分的起点设为l=1,r=n,就能过。hard版少几次。因为第一次和最后一次查询省不掉。只能优化二分的过程。对于左边界。设为 (len+1)/ 2。为什么呢,因为 len是s 到 t 的距离。rt又是 s,t之间的点。那么再去找距离rt最远 而到s和t距离和等于len 的点时,这个最大深度,必然不可能小于(len+1)/ 2啊。 也就是整条路径被rt切成两段了。而找的是更长的那条。所以左端点就优化了。 对于右端点,r不可能大于 min(len,max_dep)。dfs 的时候记录 max_dep,就行了。 因为不可能找到大于len 的点去。 那不显然不成立。 同时距离根节点最远的也就是max_dep,这样右端点就优化了。可以过hard。
AC代码:
#include <iostream>
#include <bits/stdc++.h>
#define int long long
#define mk make_pair
#define gcd __gcd
#define pb push_back
using namespace std;
const double eps = 1e-10;
//const int mod = 1e9+7;
const int mod = 998244353 ;
const int N = 3e5+7;
int n,m,k,t = 1,cas = 1;
int a[N],b[N];
int maxx = 0 ;
vector<int> edge[N];
vector<int> depp[N];
void dfs(int pos,int dep,int fa){
maxx = max(maxx,dep);
depp[dep].pb(pos);
for(int i = 0 ; i < edge[pos].size() ; i ++){
int to = edge[pos][i];
if( to != fa){
dfs(to,dep+1,pos);
}
}
}
pair<int,int> ask(vector<int> vt){
cout<<"? "<<vt.size()<<" ";
for(int i = 0 ; i < vt.size() ; i ++){
cout<<vt[i]<<" ";
}
cout<<endl;
cout.flush();
int rt,len; cin>>rt>>len;
return {rt,len};
}
signed main(){
cin>>t;
while(t--){
maxx = 0;
cin>>n;
for(int i = 1 ; i <= n ; i ++) depp[i].clear(),edge[i].clear();
for(int i = 0 ; i < n-1 ; i ++){
int x,y;
cin>>x>>y;
edge[x].pb(y);
edge[y].pb(x);
}
cout<<"? "<<n<<" ";
for(int i = 1 ; i <= n ; i ++) cout<<i<<" ";
cout<<endl;
cout.flush();
int rt,len;cin>>rt>>len;
dfs(rt,0,-1);
int l = (len+1)/2, r = min(maxx,len);
int new_rt;
while(l <= r){
int mid = (l+r)>>1;
pair<int,int> tmp = ask(depp[mid]);
if(tmp.second == len){
new_rt = tmp.first;
l = mid+1;
}else{
r = mid-1;
}
}
for(int i = 1 ; i <= n ; i ++) depp[i].clear();
dfs(new_rt,0,-1);
pair<int,int> res = ask(depp[len]);
cout<<"! "<<res.first<<" "<<new_rt<<endl;
cout.flush();
string s;
cin>>s;
}
return 0;
}