Codeforces Round #685 (Div. 2)

58 篇文章 0 订阅
35 篇文章 1 订阅

A. Subtract or Divide

题意:给定一个n。两种操作。除以一个因子(自己不算)或者自减1。求把n变成1。最少的操作次数。

思路:先特判掉1,2,3。然后分奇偶讨论。如果是偶数。那么除以n/2 就变成 2 了。然后再减1。只需2步。对于奇数。就算有奇数的因子。除一次的话,肯定没有办法变成2。最多变成3。而3变成1需要2步。也就是最少3步。而把奇数变成偶数需要1步。再变成1需要两步。所以对于所有奇数采用这种方式就行了。

AC代码:

#include <bits/stdc++.h>
using namespace std;

signed main(){
    int t;
    cin>>t;
    while(t--){
        int n;
        cin>>n;
        if(n > 3){
            if(n%2) cout<<3<<endl;
            else cout<<2<<endl;
        }else if(n== 3) cout<<2<<endl;
        else if(n == 2) cout<<1<<endl;
        else if(n == 1) cout<<0<<endl;
    }
 
}

B. Non-Substring Subsequence

题意:给一个字符串s,q次询问。询问s 的一个子串。问存不存在和子串相同的子序列。就是不能和给出的子串下标一摸一样就行了。

思路:只要有一个下标不一样就行了。因为串内可以保证他们是全相等的。如果要找出一个不同于给定子串的子序列使得和子串相等。那么左边必然包含子串的第一个字符。或者右边包含最后一个字符。那么显然,只要在子串的两边拿掉一个字符,用别的位置替代就行了。

AC代码:

#include <iostream>
#include <bits/stdc++.h>
#define int long long
#define mk make_pair
#define gcd __gcd
using namespace std;
const double eps = 1e-10;
const int mod = 1e9+7;
const int N = 3e5+7;
int n,m,k,t = 1,cas = 1;
int a[N],b[N];
 
 
signed main(){
    int t;
    cin>>t;
    while(t--){
        int q;
        cin>>n>>q;
        string s;cin>>s;
        int l,r;
        for(int i = 0 ; i < q;  i ++ ){
            cin>>l>>r; l--,r--;
            int res= 0 ;
            for(int j = 0 ; j < l ; j ++){
                if(s[l] == s[j]) res = 1;
            }
            for(int j = r+1 ; j < n ; j ++){
                if(s[j] == s[r]) res = 1;
            }
            puts(res?"YES":"NO");
        }
    }
 
}

C. String Equality

题意:一个a串,一个b串。要把a变成b。两种操作。1. 交换任意两个相邻的字符。2. 把连续相等的k个字符c变成 c+1。 问能不能 把a变成b。

思路:有了操作1。其实就是字符的位置已经没有任何关系了。因为可以通过交换变成任意的序列。那剩下的就是字符的数量。而字符又只能往上加。 所有从小字符开始判断。如果ab串的c字符数量相等。那没问题。如果不相等。如果a串少了。那无解。因为字符不能从大变小。如果 多出来的个数不能整除k。那无解。因为每次必须变动k个。

AC代码:

#include <iostream>
#include <bits/stdc++.h>
#define int long long
#define mk make_pair
#define gcd __gcd
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;
char a[N],b[N];
int cnt1[30];
int cnt2[30];
 
signed main(){
    cin>>t;
    int casn = 0;// 4968;
    while(t--){
        memset(cnt1,0,sizeof(cnt1));
        memset(cnt2,0,sizeof(cnt2));
        cin>>n>>k;
        cin>>a>>b;
        for(int i = 0 ; i < n ; i ++){
            cnt1[a[i]-'a'] ++;
            cnt2[b[i]-'a'] ++;
        }
        int res = 1;
        for(int i = 0 ; i < 26 ; i ++){
            if(cnt1[i] == cnt2[i]) continue;
            else if(cnt1[i] >= cnt2[i] && (cnt1[i] - cnt2[i]) % k == 0){
                int tmp = (cnt1[i]-cnt2[i])/k*k;
                cnt1[i+1] += tmp;
                cnt1[i] -= tmp;
            }else{
                res = 0;
            }
        }
        puts(res?"Yes":"No");
        ++cas;
    }
 
}

D. Circle Game

题意:给定一个半径d。a和b 做游戏。初始棋子在(0,0)每次只能让横坐标或者纵坐标 加k。保持 到原点的欧氏距离不大于d。谁不能移动了谁就输了。

思路:对称博弈/圆圈博弈。两个人的最优策略都是先x+k,然后y+k,也就是一直往左上角跳。那么有了这个思路。其实就很简单了。只要算出来要跳多少步。最后一步谁跳出去会输就行了。

AC代码:

#include <iostream>
#include <bits/stdc++.h>
#define int long long
#define mk make_pair
#define gcd __gcd
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;
char a[N],b[N];
 
 
signed main(){
    int t;
    cin>>t;
    while(t--){
        int d;
        cin>>d>>k;
        int k2 = k*k;
        int d2 = d*d;
        int x = 0;
        while((2*x*x*k2) <= d2) x ++;x--;
        if((x*k+k)*(x*k+k)+x*x*k2 <= d2){
            cout<<"Ashish\n";
        }else{
            cout<<"Utkarsh\n";
        }
    }
}

E. Bitwise Queries

题意:给定一个未知的数组。长度为n。值在[0,n-1]区间。可以进行询问,询问任意两个数的任意位运算的值。AND,OR,XOR。要求在n+2(easy)/n+1(hard)次操作只能给出原数组。

思路参考:https://blog.csdn.net/qq_45458915/article/details/109990408

思路:初看这个题。很容易想到。只要确定了数组中的某一个值。剩下的就可以直接异或得出。那就想办法获得a[1]的值就行了。

核心公式:a+b = (a^b)+2*(a&b)。 a ^c = (a^b)^(b^c)
有了这两个公式。我们可以选前三个数两两做异或和与运算。求出 a+b + a+c + b+c。也就是可以求出a+b+c。又因为已经求出了b+c。就可以求出a了。 但是求 a+b 要两次,b+c两次,a+c两次。就6次了。再加剩下的 n-3 个。就需要n+3次操作。 此时用公式2。就可以减少一次异或了。也就是只需要 ab,bc异或。 ac就已经是知道的了。所以总次数n+2。 E1就过了。 对于E2,要减少一次操作,就需要利用到题目给的数据的性质了。
如果 数据中有两个相同的值。那么,要么和a[1]相等,那么异或值等于0。这时候再做一次与运算。就可以求出a[1]了。 要么 a[i]^a[1] == a[j]^a[1]。这就说明a[i]和a[j]是相等的。那么,同样的。我们只需要对a[i]和a[j]做与运算,他们就求出来了。再根据和a[1]的异或值。就可以求出a[1]了。操作次数为n。
如果数据中没有重复值。又因为数据范围是[0,n-1]并且n是2的幂次。也就是说。一定会有一个数。和a[1]异或起来等于n-1,也就是二进制位全为1。并且a[1]和a[j]没有相同位。 也就是说 a[i]&a[1] == 0。有了这个再结合E1的解法。因为E1 需要两次异或和三次 与 可以求出三个值。而这里已经可以知道了其中一个 与 和两次异或 的值。 也就是再做两次与。总共5次操作。就可以求出a[1] 了。 于是就解出来。 操作次数为 n+1。

AC代码:

#include <bits/stdc++.h>
#define int long long
#define mk make_pair
using namespace std;
const int N = 1e6+7;
const int mod = 1e9+7;
int t = 1,n,m,k;
int a[N];
int tmp[N];

int ask(string op,int i,int j){
    cout<<op<<" "<<i<<" "<<j<<endl;
    cout.flush();
    int x;
    cin>>x;
    return x;
}

map<int,int> mp;
int zero = -1;
int same = -1;
int n_1 = -1;

signed main(){
    //cin>>t;
    while(t-- ){
        cin>>n;
        for(int i = 2 ; i <= n ; i ++){
            tmp[i] = ask("XOR",1,i);
            if(mp[tmp[i]]){
                same = i;
            }
            if(tmp[i] == 0){
                zero = i;
            }
            if(tmp[i] == n-1){
                n_1 = i;
            }
            mp[tmp[i]] ++;
        }
        if(zero != -1){         // 有至少一个数和a[1] 相等
            int now = ask("AND",1,zero);
            a[1] = now;
        }else if(same != -1){   // 有 a[2-n] 中有两个数相等
            for(int i = 2 ; i <= n ; i ++){
                if(tmp[i] == tmp[same]){
                    int now = ask("AND",i,same);
                    a[i] = now;
                    a[1] = tmp[i]^a[i];
                    break;
                }
            }
        }else if(n_1){          // 有一个数 异或a[1] = n-1
            int c = -1;         // 随便找一个数 c
            if(n_1+1 <= n) c = n_1+1;
            else c = n_1-1;
            int axorb = n-1,axorc = tmp[c],bxorc = axorb^axorc;
            int aandb = 0,aandc = ask("AND",1,c),bandc = ask("AND",n_1,c);
            int abc = (axorb+axorc+bxorc+2*(aandb+aandc+bandc))/2;
            int bc = bxorc+2*bandc;
            a[1] = abc - bc;
        }
        cout<<"! "<<a[1]<<" ";
        for(int i = 2 ; i <= n ; i ++){
            a[i] = tmp[i]^a[1];
            cout<<a[i]<<" ";
        }
        cout<<endl;
        cout.flush();
    }
    return 0;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值