复健训练-CF1698(Codeforces Round #803 (Div. 2))

题目在这

【写完签到题就不会了...

 

A. XOR Mixup

题意:给一个 n 个数的序列 ${a_i}$ ,其中一个数是剩下的数的异或和,让你找出这个数。

做法:直接枚举。

#include<bits/stdc++.h>
#define rep(i,x,y) for (int i=(x);i<=(y);i++)
#define ll long long

using namespace std;

const int N = 105;
int n, a[N];

void Main(){
    scanf("%d", &n);
    int XOR = 0;
    rep (i,1,n) scanf("%d", &a[i]), XOR ^= a[i];
    rep (i,1,n)
        if ((XOR^a[i]) == a[i]){printf("%d\n", a[i]); return;}
    return;
}

int main(){
    int T; scanf("%d", &T);
    while (T--) Main();
    return 0;
}
View Code

 

B. Rising Sand

题意:n 堆沙子每堆高度为 $a_i$ ,定义一堆沙子 too tall 为,$1<i<n$ 且 $a_i > a_{i-1} + a_{i+1}$ 。现在一个操作可以将一段长度为 k 的沙子的 $a_i$ 都加 1,问经过若干操作,可以最多使得多少沙子为 too tall 的。

做法:如果 $k \ge 2$ ,那么给 $a_i + 1$ 的同时势必会给 $a_{i-1}$ 或 $a_{i+1}$ 加上 1 ,那么没法使得 $a_i - a_{i-1} - a{i+1}$ 的值变得更大,所以 too tall 数量就是原来的 too tall 数量。而 $k = 1$ 时一定可以取到最大值 $\frac{n-1}{2}$ 。

#include<bits/stdc++.h>
#define rep(i,x,y) for (int i=(x);i<=(y);i++)
#define ll long long

using namespace std;

const int N = 2e5+10;
int n, k, a[N];

void Main(){
    scanf("%d%d", &n,&k);
    rep (i,1,n) scanf("%d", &a[i]);
    if (k==1){
        printf("%d\n", (n-1)/2);
    } else{
        int ans = 0;
        rep (i,2,n-1)
            if ((ll)a[i] > (ll)a[i-1]+a[i+1])
                ans++;
        printf("%d\n", ans);
    }
    return;
}

int main(){
    int T; scanf("%d", &T);
    while (T--) Main();
    return 0;
}
View Code

 

C. 3SUM Closure

题意:定义一个序列是 3SUM-closed 当且仅当对于序列中任意一个三元组的和都在序列中出现过。给一个序列问是否是 3SUM-closed 的。

做法:首先可以发现序列中如果有 $\ge 3$ 个正数或负数必定是 NO ,因为必定可以找到最大 / 小的三个正数 / 负数相加在原数列中没有出现过。而任意多的 0 可以等价为一个 0 。所以原数列转化之后最多有 5 个数。直接暴力判断一下即可。

#include<bits/stdc++.h>
#define rep(i,x,y) for (int i=(x);i<=(y);i++)
#define ll long long

using namespace std;

const int N = 2e5+10;
int n, a[N], b[10];

void Main(){
    scanf("%d", &n);
    int x = 0, y = 0, z = 0;
    rep (i,1,n){
        scanf("%d", &a[i]);
        if (a[i]>0) x ++;
        else if (a[i]<0) y ++;
        else z ++;
    }
    if (x>=3 || y >= 3){puts("NO"); return;}
    int tot = 0;
    map<int,bool> mp;
    rep (i,1,n) if (a[i]!=0) b[++tot] = a[i],mp[a[i]]=1;
    if (z) b[++tot] = 0,mp[0]=1;
    rep (i,1,tot)
        rep (j,i+1, tot)
            rep (k,j+1, tot)
                if (!mp.count(b[i]+b[j]+b[k])){puts("NO"); return;}
    puts("YES");
    return;
}

int main(){
    int T; scanf("%d", &T);
    while (T--) Main();
    return 0;
}
View Code

 

D. Fixed Point Guessing

题意:交互题,有一个 ${a} = [1..n]$ 的排列,n 为奇数,现在执行 $\frac{n-1}{2}$ 次交换,每次交换的数互不相同,交换完之后还有一个数的位置没有改变。你现在可以每次询问一个 $?\ l\ r$ ,回答会给出 $[l, r]$ 中的数从小到大排序的序列。 $n\le 10^4$ ,最多询问 15 次,要找到那个位置没有变的数。

做法:看询问次数感觉大概是 $log$ 级别的,就往二分的方向上去想。然后再思考一个区间给出了里面的数以后,我们可以知道哪些数是在 $[l,r]$ 范围内,哪些数不在。如果不在肯定是从外面交换进来的,如果在的话肯定是两两交换,而如果该区间内有我们要找的数,那么它最后是成单的,所以在 $[l,r]$ 范围内的数一定是奇数个。所以我们二分查找每次看左右两个区间哪个是在 $[l,r]$ 范围内的数是奇数个就往哪里走。不断缩小范围,最后找到那个数。

#include<bits/stdc++.h>
#define rep(i,x,y) for (int i=(x);i<=(y);i++)
#define ll long long

using namespace std;

int ask(int l,int r){
    printf("? %d %d\n", l, r);
    fflush(stdout);
    int t = 0;
    rep (i,1,r-l+1){
        int x; scanf("%d", &x);
        if (l<=x && x<=r) t ++;
    }
    return t;
}

void answer(int x){
    printf("! %d\n" , x);
    fflush(stdout);
}

void Main(){
    int n;
    scanf("%d", &n);
    int l = 1, r = n, mid;
    while (l < r){
        mid = (l+r)>>1;
        if (ask(l, mid)%2==1) r = mid;
        else l = mid+1;
    }
    answer(l);
    return;
}

int main(){
    int T; scanf("%d", &T);
    while (T--) Main();
    return 0;
}
View Code

 

E. PermutationForces II

题意:给一个 [1..n] 的排列 a 和一个 s ,现在可以进行 n 次操作,第 i 次可以选择 $i\le x\le y \le \min(i+s, n)$ 的 x y,将 ${a}$ 中 x 和 y 交换(注意不是 $a_x$ $a_y$ 交换,是值交换,以及 x 可以等于 y)。现在给出一个排列 b ,有些位置有空缺 -1 ,问有多少补全 b 的方法,使得 a 可以经过一系列操作变成 b 。

做法:【是看这个博客的题解的,写的真的很好,就是我自己想不出来..】

首先是考虑判定,假设有一个已知的 b ,要判断 a 是否能变成 b ,那么肯定是从 1 开始,看 1 最终位置在哪里,看在 a 中能否交换变到 b 中的位置。然后再看 2 , 3 .... 这样可以发现每步操作都是唯一的,而且进一步可以发现,假如把 a 按照 b 从小到大排序,相当于要把 a 变成 [1..n] 的排列,第 i 个数能否操作成功就是看 $a_i$ 是否 $\le i + s$ 。那么这里的 i 就是 $b_i$ ,所以回到原序列约束条件就是 $a_i \le b_i + s$ ,即 $b_i \ge a_i - s$ 。那么无解的情况就好判了。有解的话对于所有 -1 位置,可以算出 $b_i$ 的一个范围,根据这个范围算出答案数量,这就是个组合数学乘法原理的问题了,具体可以见代码。

#include<bits/stdc++.h>
#define rep(i,x,y) for (int i=(x);i<=(y);i++)
#define ll long long

using namespace std;

const int N = 2e5+10;
const int mod = 998244353;
int n, s, a[N], b[N], c[N], m, sum[N];

void Main(){
    scanf("%d%d", &n, &s);
    rep (i,1,n) scanf("%d", &a[i]), sum[i] = 0;
    bool flag=1;
    rep (i,1,n){
        scanf("%d", &b[i]);
        if (b[i]!=-1) sum[b[i]]++;
        if (b[i]!=-1&&b[i]<a[i]-s) flag=0;
    }
    for (int i=n-1; i>=1; i--) sum[i]+=sum[i+1];
    if (!flag){puts("0"); return;}
    m = 0;
    rep (i,1,n) if (b[i]==-1) c[++m] = max(a[i]-s, 1);
    sort(c+1, c+1+m); reverse(c+1, c+1+m);
    int ans = 1;
    rep (i,1,m) ans = (ll)ans*max(n-c[i]+1-sum[c[i]]-i+1, 0)%mod;
    printf("%d\n", ans);
    return;
}

int main(){
    int T; scanf("%d", &T);
    while (T--) Main();
    return 0;
}
View Code

 

F G 咕咕 还没看题(

posted @ 2022-07-24 01:31  bestfy  阅读(66)  评论(0编辑  收藏  举报