A.Subtract or Divide
题意:给定一个n,定义两种操作。
操作1:n=n-1;
操作2:n除以一个非自身的因数
问最少几次操作使得n变成1
题解:
特判一下n=1,2,3的情况
别的情况就偶数两次,奇数三次(奇数-1变成偶数)
int main() {
for(scanf("%d",&_);_;_--){
scanf("%d",&n);
if (n==1){puts("0");continue;}
if (n==2){puts("1");continue;}
if (n==3){puts("2");continue;}
if (n%2==0)puts("2");
else puts("3");
}
return 0;
}
B.Non-Substring Subsequence
题意:给定一个01字符串,每次给定l r,问是否有非连续的子序列使得子序列与s[l]s[l+1]…s[r]子串相等.
题解:
显然可以用子串中的n-1个元素,剩下一个用别的地方就能满足了。
这样操作是最优的,因为只需在r的后面还有一个位置x,s[x]==s[r]或者在l在前面有一个y,s[y]==s[l]即可。
所以我们只需要扫一遍记录一下第一个0,1出现的位置,最后一个0,1出现位置即可
int _,n,q,l,r;
char s[maxn];
int b[2],e[2];
int main() {
for(scanf("%d",&_);_;_--){
scanf("%d%d",&n,&q);
scanf("%s",s+1);
b[0]=b[1]=e[0]=e[1]=0;
for (int i=1;i<=n;i++)if (s[i]=='0'){b[0]=i;break;}
for (int i=1;i<=n;i++)if (s[i]=='1'){b[1]=i;break;}
for (int i=n;i>=1;i--)if (s[i]=='0'){e[0]=i;break;}
for (int i=n;i>=1;i--)if (s[i]=='1'){e[1]=i;break;}
while (q--){
scanf("%d%d",&l,&r);
if (l==b[s[l]-'0']&&r==e[s[r]-'0'])puts("NO");
else puts("YES");
}
}
return 0;
}
C.String Equality
题意:给定两个小写字母组成的串串s,t和两种操作。
操作1:交换s[i]和s[i+1]
操作2:若s[i]…s[I+k-1]都相等且不等于a,可以让这些全部都变成字母表里下一个字母
问在无限次操作后能不能使得s变成t
题解:
那显然我们可以只看每个字母的个数了(因为顺序可以通过操作1交换得到)
然后我们把s和t共有的部分抵消,剩下的就是我们要通过操作2变换得到。
首先显然抵消后每个字母数量一定都是k的倍数
然后呢我们分别看两个串的数量前缀和判断一下就能知道能不能转换成功了
bool solve(){
scanf("%d%d",&n,&k);
scanf("%s",s+1);
scanf("%s",t+1);
for (int i=0;i<26;i++)num1[i]=num2[i]=0;
for (int i=1;i<=n;i++)num1[s[i]-'a']++;
for (int i=1;i<=n;i++)num2[t[i]-'a']++;
for (int i=0;i<26;i++){
int cnt=min(num1[i],num2[i]);
num1[i]-=cnt;
num2[i]-=cnt;
if (num1[i]%k||num2[i]%k)return false;
num1[i]/=k;num2[i]/=k;
}
if (num1[0]<num2[0])return false;
for (int i=1;i<26;i++){
num1[i]+=num1[i-1];
num2[i]+=num2[i-1];
if (num1[i]<num2[i])return false;
}
return true;
}
int main() {
for(scanf("%d",&_);_;_--){
printf("%s\n",solve()?"Yes":"No");
}
return 0;
}
D.Circle Game
题意:在一个二维平面内,有一个硬币初始在(0,0),两个人玩游戏,每个人每次只能选择让硬币的横坐标+k或者纵坐标+k,硬币到原点的欧几里得距离要小于等于d,谁不能操作就输了,问谁赢。
题解:
若当前是后手赢的状态,那么不管第一个人怎么操作,我都反着来。最后会到达一个(x,x),必须要(x+k,x)和(x,x+k)都不能走,那就是后手赢。
若是先手赢,先手先向右走,然后都跟后手反着来,最后会到达(x+k,x)且(x+k,x+k)和(x+2k,x)都不能走,就是先手赢。
所以就是简单的判断一下(x+k,x)这个点到底在不在圆内。
ll _,d,k;
bool solve(){
scanf("%lld%lld",&d,&k);
ll mx=0;
for (int i=1;i<=100000;i++){
if (1ll*i*k*i*k+1ll*i*k*i*k<=1ll*d*d)mx=i*k;
else break;
}
if (mx*mx+(mx+k)*(mx+k)>d*d)return false;
return true;
}
int main() {
for(scanf("%lld",&_);_;_--){
printf("%s\n",solve()?"Ashish":"Utkarsh");
}
return 0;
}
E.Bitwise Queries
题意:交互题,给定n(n保证为2的幂次),你可以询问a[x]和a[y]按位与,按位或,按位异或的值。E1要求n+2次完成,E2要求n+1次完成,题目保证a[i]∈\in∈[0,n-1]
题解:
由于我直接推出了n+1次的做法就直接讲n+1次做法了。
我们观察这个n个数无非就是两种情况。
第一种,存在a[x]=a[y]
第二种,不存在a[x]=a[y]
首先我们要有两个前置芝士
若x^y=z 则 x^z=y, y^z=x
若x^y=z ,x^k=z 则y=k
我们先用掉n-1次操作先问a[1]与a[i]的值,如果出现0说明a[i]=a[1],那么我们问一下a[1]和a[x]按位与的结果,就得到了a[1]然后就得到了全部的值
如果出现了两个相同的值也说明这两个位子的数相等,然后我们询问这两个位子的数得到了它们的值反推出a[1]然后就得到了全部的值,这种情况只用n次操作就可完成
如果没有出现上述两种情况,说明全部的数唯一的。
这个时候我们去找跟a[1]异或为1的数,设为a[x]好了,a[x]跟a[1]除了最后一位不同,别的位都一样,所以我们先询问一下a[1] AND a[x] 那就是有一个问题到底是a[1]最后一位有1还是a[x]最后一位有1呢。这个时候我们去找跟a[1]异或为3的数字,设为a[y]吧,我们询问a[x] AND a[y],若是1说明这个1在x上,若是0说明在y上(因为a[x]和a[y]在最后一位是相同的)至此我们得到了a[1]同理得到全部的数,这种情况只需n+1次操作
所以总的操作次数是n+1次
int main() {
int flag=0;
scanf("%d",&n);
for (int i=2;i<=n;i++){
printf("XOR %d %d\n",1,i);
fflush(stdout);
scanf("%d",&v[i]);
if (flag==0&&p[v[i]]){
flag=1;
s1=p[v[i]];
s2=i;
}
else if (flag==0&&v[i]==0){
flag=1;
s1=1;s2=i;
}
p[v[i]]=i;
}
if (flag){
printf("AND %d %d\n",s1,s2);
fflush(stdout);
scanf("%d",&ans[s1]);
ans[s2]=ans[s1];
ans[1]=v[s1]^ans[s1];
for (int i=2;i<=n;i++){
if (i!=s1&&i!=s2)ans[i]=v[i]^ans[1];
}
printf("!");
for (int i=1;i<=n;i++)printf(" %d",ans[i]);puts("");
}
else{
printf("AND %d %d\n",1,p[1]);
fflush(stdout);
scanf("%d",&ans[1]);
printf("AND %d %d\n",p[1],p[3]);
fflush(stdout);
int cnt;
scanf("%d",&cnt);
if ((cnt&1)==0)ans[1]+=1;
for (int i=2;i<=n;i++){
ans[i]=v[i]^ans[1];
}
printf("!");
for (int i=1;i<=n;i++)printf(" %d",ans[i]);puts("");
}
return 0;
}