[[https://codeforces.com/contest/1370|Codeforces Round #651 (Div. 2)]]
====== A. Maximum GCD ======
$1$到$n$中任选两个数字$a,b$,能够得到的最大$\gcd(a,b)$
题解:$\gcd(\lfloor\frac n 2 \rfloor,2·\lfloor\frac n 2 \rfloor)=\lfloor\frac n 2 \rfloor$
#include
#include
#include
using namespace std;
int main()
{
int t;
scanf("%d",&t);
while(t--)
{
int n;
scanf("%d",&n);
printf("%d\n",n/2);
}
return 0;
}
\\
====== B. GCD Compression ======
数组$a$大小为$2n$,先任选两个数字丢掉,再将其余数字两两一组变成二者的和化为大小$n-1$的数组$b$。要求使得数组$b$的最大公因数不为$1$。
题解:两奇数相加得偶数,两偶数相加还是偶数。只要使得奇、偶数数目为偶数个即可。故如果初始奇、偶数为偶数个,扔掉两奇数或两偶数;初始奇、偶数为奇数个,扔掉一奇一偶。最大公因数$2$
#include
#include
#include
#include
using namespace std;
int a[5005];
stacke,o;
int main()
{
int t;
scanf("%d",&t);
while(t--)
{
int n;
scanf("%d",&n);
for(int i=1;i<=2*n;i++)
{
scanf("%d",&a[i]);
if(a[i]&1)o.push(i);
else e.push(i);
}
if(o.size()&1)o.pop(),e.pop();
else if(o.size()>=2)o.pop(),o.pop();
else e.pop(),e.pop();
while(!o.empty())
{
printf("%d ",o.top());
o.pop();
printf("%d\n",o.top());
o.pop();
}
while(!e.empty())
{
printf("%d ",e.top());
e.pop();
printf("%d\n",e.top());
e.pop();
}
}
return 0;
}
\\
====== C. Number Game ======
初始给出正整数$n$,两玩家轮流操作,每次可以选择在数字大于一的情况下减一或除以大于一的奇因子。无法操作者败
题解:分类讨论。
- $n$为奇数。除以它本身先手必胜($1$是特例,必败)。
- $n$为偶数且没有大于一的奇因子。先手只能减一之后对方得到上一种局面必胜,故必败($2$是特例,必胜)。
- $n$为偶数有大于一的奇因子。先手将奇因子除掉将第二种局面交给对方,故如果因子$2$个数不为一,先手必胜。而当因子$2$个数为一时,若奇因子个数为一,$2·p$减一变奇数必败,除以奇因子必败,总之必败;若奇因子个数不为一,可以除以部分奇因子将$2·p$的局面交给对方以获得必胜。
#include
#include
#include
using namespace std;
int main()
{
int t;
scanf("%d",&t);
while(t--)
{
int n;
scanf("%d",&n);
if(n==1){puts("FastestFinger");continue;}
if(n==2){puts("Ashishgup");continue;}
if(n&1){puts("Ashishgup");continue;}
int cnt1=0,cnt2=0;
while(n%2==0)n/=2,cnt2++;
for(int i=3;i*i<=n;i+=2)
{
while(n%i==0)n/=i,cnt1++;
}
if(n!=1)cnt1++;
if((cnt2>1&&cnt1>0)||cnt1>1)puts("Ashishgup");
else puts("FastestFinger");
}
return 0;
}
\\
====== D. Odd-Even Subsequence ======
给定一个长度为$n$的数组$a$,可以选择长度为$k$的子序列(重新标号,从$1$开始),请最小化$\min(\max(s_1, s_3, s_5, \ldots), \max(s_2, s_4, s_6, \ldots))$。
题解:这个长得还挺二分的哦。check某个$\text{mid}$能否成立的时候,分成使得奇数位满足和使得偶数位满足两种,贪心地看能否凑够子序列长度。
#include
#include
#include
using namespace std;
int n,k,a[200005];
bool check(int x)
{
int j=1;
for(int i=1;i<=n;i++)
{
if((j&1)&&a[i]<=x)j++;
else if((j&1)==0)j++;
if(j>k)return true;
}
j=1;
for(int i=1;i<=n;i++)
{
if((j&1)==0&&a[i]<=x)j++;
else if(j&1)j++;
if(j>k)return true;
}
return false;
}
int main()
{
scanf("%d%d",&n,&k);
int l=1000000000,r=1,ans;
for(int i=1;i<=n;i++)scanf("%d",&a[i]),l=min(l,a[i]),r=max(r,a[i]);
while(l<=r)
{
int mid=(l+r)>>1;
if(check(mid))r=mid-1,ans=mid;
else l=mid+1;
}
printf("%d\n",ans);
return 0;
}
\\
====== E. Binary Subsequence Rotation ======
给出长度为$n$的$01$字符串$s,t$,每次操作可以选一个子序列顺时针旋转,问最少几次从$s$变到$t$。
题解:能够变换成功当仅当两串为anagram。显然满足$s_i=t_i$的位置是不需要被考虑的。而观察发现剩余位置里每次旋转形如$\mathsf{01010101、101010}$的子序列是最优的,可以贪心地扫一遍详见代码。
#include
using namespace std;
const int N=1e6+10;
int n,cnt1,cnt2;
char s[N],t[N];
int main()
{
scanf("%d%s%s",&n,s,t);
for(int i=0;s[i];i++)if(s[i]=='1')cnt1++;
for(int i=0;t[i];i++)if(t[i]=='1')cnt2++;
if(cnt1!=cnt2)puts("-1");
else
{
int w0=0,w1=0; //waiting for 0\waiting for 1
for(int i=0;s[i];i++)
if(s[i]!=t[i])
{
if(s[i]=='0')
{
if(w0)w0--;
w1++;
}
else
{
if(w1)w1--;
w0++;
}
}
printf("%d\n",w0+w1);
}
return 0;
}
\\
====== F. The Hidden Pair ======
交互。给出一棵$n(2 \le n \le 1000)$个节点的树,对方选定两个节点$s,f$给你猜。你可以做出若干询问,询问给出一个点集$a$,对方会提供点集中到$s,f$距离之和最小的点和这个距离。
easy version最多$14$问,hard version $11$问。
题解:我还挺喜欢这个题的,赛时wa了E看这个还挺有思路的,刚刚好写完,结果莫名其妙的wa了就结束了。后来发现是打错了某个地方的变量名,,,
首先可以想到,询问全部点,距离之和最小的只能是在$s$到$f$路径上,且距离之和是$d_0=dist(s,f)$,于是我们得到一个$sf$链上的点$x_0$。问题数量差不多是$\log n$级别的,于是想到二分。二分得到的$d_0$,每次dfs得到距离$x_0$为$mid$的点集询问,找出最远的使得$d=d_0$的距离及点$x_1$,则$x_1$是距离$x_0$较远一端的端点(得到答案之一)。之后再询问距离$x_1$为$d_0$的点集得到另一个答案$x_2$。
其间,二分区间如果是$[1,d_0]$可能会在二分过程中消耗$10$个问题,总问题数$12$过不了hard version。可以想到距离链上一点较远的点距离肯定大于链长之半,所以二分$[\frac {d_0} 2,d0]$可以恰控制在$11$次询问内。
#include
#include
#include
#include
using namespace std;
int n,head[1005],cnt;
char s[100];
vectorv;
struct Node
{
int nxt,to;
}Edges[5005];
void addedge(int u,int v)
{
Edges[++cnt].nxt=head[u];
head[u]=cnt;
Edges[cnt].to=v;
}
void dfs(int u,int f,int d)
{
if(d==0){v.push_back(u);return;}
for(int i=head[u];~i;i=Edges[i].nxt)
{
int v=Edges[i].to;
if(v!=f)dfs(v,u,d-1);
}
}
int main()
{
int t;
scanf("%d",&t);
while(t--)
{
scanf("%d",&n);
memset(head,-1,sizeof(head));
cnt=0;
for(int i=1;i>1;
dfs(x0,0,mid);
if(v.size()==0){r=mid-1;continue;}
printf("? %d ",v.size());
for(int i=0;i>s;
}
return 0;
}
\\