Codeforces Round 897 (Div. 2)

Codeforces Round 897 (Div. 2)

A. green_gold_dog, array and permutation

分析:

由题意:

\[c_i = a_i - b_i \]

\(c_i\)种类最多就是\(n\)个数都不同。

\(a_i\)不断变大,\(b_i\)不断变小,那么\(c_i\)会不断变大。

代码:

#include <bits/stdc++.h>
using namespace std;
using ll = long long;
typedef pair<int, int> pii;
typedef pair<pair<ll, ll>, ll> piii;
#define fi first
#define se second
using i128 = __int128;
int n, m;

int lowbit(int x)
{
    return x & -x;
}

void solve()
{
    scanf("%d", &n);
    vector<pii> a(n + 1);
    for (int i = 1; i <= n; i++)
    {
        scanf("%d", &a[i].fi);
        a[i].se = i;
    }
    sort(a.begin() + 1, a.end());
    vector<int> b(n + 1);
    int cnt = n;
    for (int i = 1; i <= n; i++)
    {
        b[a[i].se] = cnt--;
    }
    for (int i = 1; i <= n; i++)
    {
        printf("%d ", b[i]);
    }
    printf("\n");
}

int main()
{
    int t = 1;
    scanf("%d", &t);
    while (t--)
    {
        solve();
    }
    return 0;
}

B. XOR Palindromes

分析:

如何获得回文串:

  1. 对半分,通过操作使左右两侧一样。
  2. 全部变成1或0

注意,如果总字符数为奇数,那么每个左右两边一样后,我们还可以将中间的字符进行一次翻转操作。

使得左右两边一样,除了将两边不同的变成相同的,还可以将两边相同的同时翻转。

代码:

#include <bits/stdc++.h>
using namespace std;
using ll = long long;
typedef pair<int, int> pii;
typedef pair<pair<ll, ll>, ll> piii;
#define fi first
#define se second
using i128 = __int128;
int n, m;

int lowbit(int x)
{
    return x & -x;
}

void solve()
{
    scanf("%d", &n);
    string s;
    cin >> s;
    int l = 0;
    int r = s.size() - 1;
    int cnt = 0;
    while (l < r)
    {
        if (s[l] != s[r])
        {
            cnt++;
        }
        l++;
        r--;
    }
    unordered_map<int, int> q;
    for (int i = 0; i < n; i++)
    {
        int x = s[i] - '0';
        q[x]++;
    }
    string ans;
    for (int i = 0; i <= n; i++)
    {
        ans += '0';
    }
    for (int i = cnt; i <= n / 2; i++)
    {
        if (i == cnt)
        {
            ans[i] = '1';
        }
        else
        {
            int t = i - cnt;
            ans[cnt + t * 2] = '1';
        }
    }
    if (n & 1)
    {
        for (int i = n - 1; i >= 0; i--)
        {
            if (ans[i] == '1')
            {
                ans[i + 1] = '1';
            }
        }
    }

    ans[q[0]] = '1';
    ans[q[1]] = '1';
    cout << ans << endl;
}

int main()
{
    int t = 1;
    scanf("%d", &t);
    while (t--)
    {
        solve();
    }
    return 0;
}

C. Salyg1n and the MEX Game

分析:

直接贪心了,第一次添加\(MEX(S)\),之后删了啥加啥。

代码:

#include <bits/stdc++.h>
using namespace std;
using ll = long long;
typedef pair<int, int> pii;
typedef pair<pair<ll, ll>, ll> piii;
#define fi first
#define se second
using i128 = __int128;
int n, m;

int lowbit(int x)
{
    return x & -x;
}

void solve()
{
    scanf("%d", &n);
    vector<int> a(n + 1);
    for (int i = 1; i <= n; i++)
    {
        scanf("%d", &a[i]);
    }
    int cur = 0;
    for (int i = 1; i <= n; i++)
    {
        if (a[i] == cur)
        {
            cur++;
        }
        else
        {
            break;
        }
    }
    while (true)
    {
        printf("%d\n", cur);
        fflush(stdout);
        scanf("%d", &cur);
        if (cur == -1)
        {
            return;
        }
    }
}

int main()
{
    int t = 1;
    scanf("%d", &t);
    while (t--)
    {
        solve();
    }
    return 0;
}

D - Cyclic Operations

分析:

如果\(k = 1\),那么,所有\(a_i = i\),否则就寄了。

如果\(k \neq 1\),那么,我们让\(i\)\(a_i\)连边,若构成的基环树上的环中点数等于\(k\)就可以,否则就寄了。

注意:可能有多个基环树。如果后面构成的树连着连着连到了之前出现过的基环树上,那么同样合法。

代码:

#include <bits/stdc++.h>
using namespace std;
using ll = long long;
typedef pair<int, int> pii;
#define fi
#define se

void solve()
{
    int n, k;
    scanf("%d %d", &n, &k);
    vector<int> a(n + 1);
    for (int i = 1; i <= n; i++)
    {
        scanf("%d", &a[i]);
    }
    if (k == 1)
    {
        for (int i = 1; i <= n; i++)
        {
            if (a[i] != i)
            {
                puts("NO");
                return;
            }
        }
        puts("YES");
        return;
    }
    vector<int> st(n + 1, -1);
    for (int i = 1; i <= n; i++)
    {
        int cur = i;
        while (st[cur] == -1)
        {
            st[cur] = i;
            cur = a[cur];
        }
        // 连到之前树上直接ok
        if (st[cur] == i)
        {
            int j = cur;
            int len = 0;
            do
            {
                len++;
                j = a[j];
            } while (j != cur);
            if (len != k)
            {
                puts("NO");
                return;
            }
        }
    }
    puts("YES");
}
int main()
{
    int t = 1;
    cin >> t;
    while (t--)
    {
        solve();
    }
    return 0;
}

E. Salyg1n and Array (hard version)

分析:

\(k\)最大只有\(50\),所以,如果\(n \% k == 0\),直接暴力遍历即可。

否则我们只需要处理最后一段,即\(n - (n \%k) \sim n\))$这一段。

由于\(n,k\)都是偶数,那么最后这段长度也一定会是偶数。

假设\(k = 6\),最后这一段长度为\(4\),我们这里就要用到题中查询一段,这一段就会翻转的性质。

假设此时最后\(8\)个数是\([1,2,3,4,5,6,7,8]\)

我们的目标是让\(ans = ans \oplus 5 \oplus 6\oplus 7\oplus8\)

我们先异或上[1…6]这段区域,此时序列变为\([6,5,4,3,2,1,7,8]\),然后我们在异或上最后\(k\)个数即\([4,3,2,1,7,8]\)

不难发现,\([1,2,3,4]\)被异或了两次,所以得到了答案。

代码:

#include<bits/stdc++.h>
using namespace std;
using ll = long long;
typedef pair<int,int> pii;
#define fi
#define se

void solve()
{
	int n,k;
	cin>>n>>k;
	int idx = 1;
	ll ans = 0;
	while(idx + k - 1 <= n)
	{
		printf("? %d\n",idx);
		fflush(stdout) or cout.flush();
		int x;
		cin>>x;
		idx += k;
		ans ^= x;
	}
	if(idx == n + 1)
	{
		printf("! %d\n",ans);
		fflush(stdout) or cout.flush();
		return;
	}
	int res = n - idx + 1;
	int t = res / 2;
	printf("? %d\n",n - t - k + 1);
	fflush(stdout) or cout.flush();
	int x;
	cin>>x;
	ans ^= x;
	printf("? %d\n",n - k + 1);
	fflush(stdout) or cout.flush();
	cin>>x;
	ans ^= x;
	printf("! %d\n",ans);
	fflush(stdout) or cout.flush();
	
}
int main()
{
	int t = 1;
	cin >> t;
	while(t--)
	{
		solve();
	}
	return 0;
}
posted @ 2023-09-12 17:14  value0  阅读(44)  评论(0)    收藏  举报