Codeforces Round 970 (Div. 3)

A. Sakurako's Exam

发现 \(1\) 比较万能,先让 \(2\) 相互抵消,再让 \(1\) 去抵消 \(2\) ,最后 \(1\) 相互抵消

点击查看代码
#include<bits/stdc++.h>
using namespace std;

#define ll long long
#define ull unsigned long long

int read()
{
    int x = 0; bool f = false; char c = getchar();
    while(c < '0' || c > '9') f |= (c == '-'), c = getchar();
    while(c >= '0' && c <= '9') x = (x << 1) + (x << 3) + (c & 15), c = getchar();
    return f ? -x : x;
}

const int N = 1e5 + 5;

void solve()
{
    int a = read(), b = read();
    b %= 2;
    if(b)
    {
        if(a >= 2 && (a - 2) % 2 == 0) printf("YES\n");
        else printf("NO\n");
    }else
    {
        if(a % 2 == 0) printf("YES\n");
        else printf("NO\n");
    }
}

int main()
{
    int T = read();
    while(T--) solve();
    return 0;
}

B. Square or Not

直接模拟判断

点击查看代码
#include<bits/stdc++.h>
using namespace std;

#define ll long long
#define ull unsigned long long

int read()
{
    int x = 0; bool f = false; char c = getchar();
    while(c < '0' || c > '9') f |= (c == '-'), c = getchar();
    while(c >= '0' && c <= '9') x = (x << 1) + (x << 3) + (c & 15), c = getchar();
    return f ? -x : x;
}

const int N = 2e5 + 5;
char s[N];

void solve()
{
    int n = read();
    scanf("%s", s + 1);
    int Size = sqrt(n);
    if(Size * Size != n){ printf("No\n"); return ; }
    for(int i = 1; i <= Size; ++i)
        for(int j = 1; j <= Size; ++j)
        {
            int flag = 0;
            if(i == 1 || i == Size || j == 1 || j == Size) flag = 1;
            if(s[(i - 1) * Size + j] - '0' != flag){ printf("No\n"); return ; }
        }
    printf("Yes\n");
}

int main()
{
    int T = read();
    while(T--) solve();
    return 0;
}

C. Longest Good Array

贪心的令相邻数的间隔最小且递增,即 $1, 2, 3, \cdots $

\(1\) 个数为 \(l\) ,第 \(n + 1\) 个数为 \(l + \frac{n(n+1)}{2} \le r\) ,二分答案 \(n\) 即可

点击查看代码
#include<bits/stdc++.h>
using namespace std;

#define ll long long
#define ull unsigned long long

int read()
{
    int x = 0; bool f = false; char c = getchar();
    while(c < '0' || c > '9') f |= (c == '-'), c = getchar();
    while(c >= '0' && c <= '9') x = (x << 1) + (x << 3) + (c & 15), c = getchar();
    return f ? -x : x;
}

const int N = 2e5 + 5;
char s[N];

void solve()
{
    int l = read(), r = read(), len = r - l;
    int L = 0, R = 100000;
    while(L < R)
    {
        int mid = (L + R + 1) >> 1;
        if(1ll * mid * (mid + 1) / 2 <= len) L = mid;
        else R = mid - 1;
    }
    printf("%d\n", L + 1);
}

int main()
{
    int T = read();
    while(T--) solve();
    return 0;
}

D. Sakurako's Hobby

同一个置换环上的点可以相互到达,并查集合并维护置换环上的点即可

点击查看代码
#include<bits/stdc++.h>
using namespace std;

#define ll long long
#define ull unsigned long long

int read()
{
    int x = 0; bool f = false; char c = getchar();
    while(c < '0' || c > '9') f |= (c == '-'), c = getchar();
    while(c >= '0' && c <= '9') x = (x << 1) + (x << 3) + (c & 15), c = getchar();
    return f ? -x : x;
}

const int N = 2e5 + 5;
int p[N], c[N];
char s[N];
int f[N], Size[N], Size0[N], Size1[N];

int find(int x){ return (x == f[x]) ? (f[x]) : (f[x] = find(f[x])); }
void merge(int x, int y)
{
    x = find(x), y = find(y);
    if(x == y) return ;
    Size[x] += Size[y], f[y] = x;
    Size0[x] += Size0[y], Size1[x] += Size1[y];
}

void solve()
{
    int n = read();
    for(int i = 1; i <= n; ++i) f[i] = i, Size[i] = 1;
    for(int i = 1; i <= n; ++i) p[i] = read();
    scanf("%s", s + 1);
    for(int i = 1; i <= n; ++i)
    {
        if(s[i] == '0') Size0[i] = 1, Size1[i] = 0;
        else Size1[i] = 1, Size0[i] = 0;
    }
    for(int i = 1; i <= n; ++i) merge(i, p[i]);
    for(int i = 1; i <= n; ++i)
    {
        int x = find(i);
        printf("%d ", Size0[x]);
    }
    printf("\n");
}

int main()
{
    int T = read();
    while(T--) solve();
    return 0;
}

E. Alternating String

发现 \(n\) 为奇数时一定需要一次 \(1\) 操作,偶数时一定不需要 \(1\) 操作

对于 \(n\) 为偶数,先各自记录奇数位和偶数位每个字符出现的情况,然后分别枚举奇数位和偶数位的最终字符

对于 \(n\) 为奇数,先枚举删去哪个点,最终序列的奇数位是这个点前面的奇数位和后面的偶数位,再分别枚举奇数位和偶数位的最终字符,记 \(odd[i][j]\) 为前 \(i\) 个数,奇数位字符为 \(j\) 的个数,\(even[i][j]\) 同理

点击查看代码
#include<bits/stdc++.h>
using namespace std;

#define ll long long
#define ull unsigned long long

int read()
{
    int x = 0; bool f = false; char c = getchar();
    while(c < '0' || c > '9') f |= (c == '-'), c = getchar();
    while(c >= '0' && c <= '9') x = (x << 1) + (x << 3) + (c & 15), c = getchar();
    return f ? -x : x;
}

const int N = 2e5 + 5;
char s[N];
int odd[N][26], even[N][26];

void solve()
{
    int n = read();
    scanf("%s", s + 1);
    for(int i = 1; i <= n; ++i)
    {
        for(int j = 0; j <= 25; ++j) odd[i][j] = even[i][j] = 0;
        if(i & 1) odd[i][s[i] - 'a'] = 1;
        else even[i][s[i] - 'a'] = 1;
        for(int j = 0; j <= 25; ++j) odd[i][j] += odd[i - 1][j], even[i][j] += even[i - 1][j];
    }
    int ans = 0x7fffffff;
    if(n & 1)
    {
        for(int i = 1; i <= n; ++i)
        {
            for(int j = 0; j <= 25; ++j)
                for(int k = 0; k <= 25; ++k)
                {
                    int x = odd[i - 1][j] + even[n][j] - even[i][j];
                    int y = even[i - 1][k] + odd[n][k] - odd[i][k];
                    ans = min(ans, n - 1 - x - y);
                }
        }
        printf("%d\n", ans + 1);
    }else
    {
        for(int i = 0; i <= 25; ++i)
            for(int j = 0; j <= 25; ++j)
                ans = min(ans, n - (odd[n][i] + even[n][j]));
        printf("%d\n", ans);
    }
}

int main()
{
    int T = read();
    while(T--) solve();
    return 0;
}

F. Sakurako's Box

\[ans = \frac{\sum_{i < j} a_i \times a_j}{C_{n}^{2}} \]

考了个逆元?

点击查看代码
#include<bits/stdc++.h>
using namespace std;

#define ll long long
#define ull unsigned long long

int read()
{
    int x = 0; bool f = false; char c = getchar();
    while(c < '0' || c > '9') f |= (c == '-'), c = getchar();
    while(c >= '0' && c <= '9') x = (x << 1) + (x << 3) + (c & 15), c = getchar();
    return f ? -x : x;
}

const ll mod = 1e9 + 7;
const int N = 2e5 + 5;
ll n, a[N];

ll qpow(ll a, ll b, ll mod)
{
    ll ans = 1;
    while(b)
    {
        if(b & 1) ans = ans * a % mod;
        b >>= 1;
        a = a * a % mod;
    }
    return ans;
}

void solve()
{
    n = read();
    for(int i = 1; i <= n; ++i) a[i] = read();
    ll sum = 0;
    for(int i = 1; i <= n; ++i) sum += a[i];
    ll ans = 0;
    for(int i = 1; i <= n; ++i)
    {
        sum -= a[i];
        ans += sum % mod * a[i] % mod;
    }
    printf("%lld\n", ans % mod * qpow(1ll * n * (n - 1) / 2 % mod, mod - 2, mod) % mod);
}   

int main()
{
    int T = read();
    while(T--) solve();
    return 0;
}

G. Sakurako's Task

考虑三个数 \(x, y, z\) 可以反复操作 \(x, y\) 得到 \(\gcd(x, y)\) ,再将其与 \(z\) 反复操作,得到 \(\gcd(x, y, z)\)

因此可以通过操作将所有的数改为全体的 \(\gcd\)

而相同的数可以做差得到 \(0\) ,也可以做和得到若干倍,贪心的想,充分利用每个数,各不相同,且尽量小,能得到的最小的序列为 \(0, \gcd, 2\gcd, 3\gcd, \cdots\)

\(K\) 插空即可,\(n = 1\) 时需特判

点击查看代码
#include<bits/stdc++.h>
using namespace std;

#define ll long long
#define ull unsigned long long

int read()
{
    int x = 0; bool f = false; char c = getchar();
    while(c < '0' || c > '9') f |= (c == '-'), c = getchar();
    while(c >= '0' && c <= '9') x = (x << 1) + (x << 3) + (c & 15), c = getchar();
    return f ? -x : x;
}

const int N = 2e5 + 5;
int n, K, a[N];

int gcd(int x, int y)
{
    if(!y) return x;
    return gcd(y, x % y);
}

void solve()
{
    n = read(), K = read();
    int d = 0;
    for(int i = 1; i <= n; ++i)
    {
        a[i] = read();
        if(!d) d = a[i];
        if(d) d = gcd(d, a[i]); 
    }

    if(n == 1)
    {
        if(K <= a[1]) printf("%d\n", K - 1);
        else printf("%d\n", K);
        return ;
    }

    if(d == 1)
    {
        printf("%d\n", n - 1 + K);
    }else
    {
        int sum = (int)ceil(1.0 * K / (d - 1));
        sum = min(sum, n);
        printf("%d\n", sum + K - 1);
    }
}       

int main()
{
    int T = read();
    while(T--) solve();
    return 0;
}

H. Sakurako's Test

每次选一个数变小,问最小的中位数?直接把每个数变到最小,即对 \(x\) 取模

现在需要快速找到对 \(x\) 取模为某个值的数的个数?

考虑根号分治

对于 \(x \le \sqrt{n}\) ,可以 \(O(n\sqrt{n})\) 预处理出对 \(cnt[i][j]\) ,对 \(i\) 取模余数小于等于 \(j\) 的数的个数

对于 \(x >\sqrt{n}\) , 将序列分为 \(\frac{n}{x} (\le \sqrt{n})\) 段,先二分答案,前缀和处理后,每一段都可以 \(O(1)\) 得到前 \(i\) 个数

总复杂度为 \(O(n\sqrt{n}) + O(n\sqrt{n} \log n)\)

多测可能会重复询问一个数,最好对答案记忆化一下

更优的解法:

不考虑根号分治,对所有的 \(x\) 都用 \(x > \sqrt{n}\) 的方式处理,发现需要

\[\sum \frac{n}{x} \sim n\ln n \sim n\log n \]

算上二分总复杂度为 \(O(n \log^2 n)\)

总结

  • 当操作次数为 \(\frac{n}{x}\) 时可以看看是不是调和级数
根号分治代码
#include<bits/stdc++.h>
using namespace std;

#define ll long long
#define ull unsigned long long

int read()
{
    int x = 0; bool f = false; char c = getchar();
    while(c < '0' || c > '9') f |= (c == '-'), c = getchar();
    while(c >= '0' && c <= '9') x = (x << 1) + (x << 3) + (c & 15), c = getchar();
    return f ? -x : x;
}

const int N = 1e5 + 5;
int n, q;
int a[N];

int cnt[450][450];
int ssum[N];

void solve()
{
    n = read(), q = read();
    for(int i = 0; i <= n; ++i) ssum[i] = 0;
    for(int i = 1; i <= n; ++i) a[i] = read(), ssum[a[i]]++;
    for(int i = 1; i <= n; ++i) ssum[i] += ssum[i - 1];
    int Size = sqrt(n);
    for(int i = 1; i <= Size; ++i)
        for(int j = 0; j < i; ++j)
            cnt[i][j] = 0;
    for(int i = 1; i <= Size; ++i)
    {
        for(int j = 1; j <= n; ++j)
            cnt[i][a[j] % i]++;
        for(int j = 1; j < i; ++j)
            cnt[i][j] += cnt[i][j - 1]; // 以i为模数,余数小于等于j的数的个数
    }
    int MID = 0;
    if(n % 2 == 0) MID = (n + 2) / 2;
    else MID = (n + 1) / 2;
    while(q--)
    {
        int x = read();
        if(x <= Size)
        {
            int ans = 0;
            for(int i = 0; i < x; ++i)
            {
                if(cnt[x][i] < MID) continue;
                else{ ans = i; break; }
            }
            printf("%d ", ans);
        }else
        {
            int l = 0, r = x - 1;
            while(l < r)
            {
                int mid = (l + r) >> 1;
                int num = 0, now = mid;
                for(int i = 0; i <= n; i += x, now += x)
                {
                    num += ssum[min(now, n)];
                    if(i) num -= ssum[i - 1];
                }
                if(num < MID) l = mid + 1;
                else r = mid;
            }
            printf("%d ", l);
        }
    }
    printf("\n");
}   

int main()
{
    int T = read();
    while(T--) solve();
    return 0;
}
posted @ 2024-09-02 08:06  梨愁浅浅  阅读(1222)  评论(4编辑  收藏  举报