跳转至

ABC347(A-E)

A - Divisible

题目大意

给你 \(N\) 个整数 \(A = (A_1, A_2, \cdots, A_N)\) 和一个整数 \(K\) ,如果 \(A_i\)\(K\) 的倍数,输出 \(\frac{A_i}{K}\)

参考代码
#include <iostream>

using namespace std;

int main()
{
    int n, k, x;
    cin >> n >> k;
    while(n--)
    {
        cin >> x;
        if(x % k == 0)
            cout << x / k << ' ';
    } 
    return 0;
}
import java.util.Scanner;

public class Main {

    public static void main(String[] args) {
        Scanner in = new Scanner(System.in);
        int n = in.nextInt();
        int k = in.nextInt();
        while (n-- > 0) {
            int x = in.nextInt();
            if (x % k == 0)
                System.out.printf("%d ", x / k);
        }
        in.close();
    }
}

B - Substring

题目大意

给你一个字符串 \(S\),求出 \(S\) 有多少个不同的子串。

参考代码
#include <iostream>
#include <unordered_set>

using namespace std;

int main()
{
    string s;
    cin >> s;
    int n = s.size();
    unordered_set<string> vis;
    for(int i = 0; i < n; i++)
    {
        string now;
        for(int j = i; j < n; j++)
        {
            now += s[j];
            vis.insert(now);
        }
    }
    cout << vis.size() << endl;
    return 0;
}
import java.util.HashSet;
import java.util.Scanner;

public class Main {

    public static void main(String[] args) {
        Scanner in = new Scanner(System.in);
        char[] s = in.next().toCharArray();
        int n = s.length;
        HashSet<String> vis = new HashSet<>();
        for (int i = 0; i < n; i++) {
            String now = "";
            for (int j = i; j < n; j++) {
                now += s[j];
                vis.add(now);
            }
        }
        System.out.println(vis.size());
        in.close();
    }
}

C - Ideal Holidays

题目大意

假设一个星期有 \(A+B\) 天,其中前 \(A(A \le 10^9)\) 天是假期,接下来的 \(B(B \le 10^9)\) 天是工作日。有 \(N(N \le 2 \times 10^5)\) 个计划,其中第 \(i\) 个计划是在 \(D_i\) 天后。你忘记了今天是一星期中哪一天。问:是否存在一种可能,使得这 \(N\) 个计划都能在假日完成?

解题思路

每一天我们实际只关心是一个星期的第几天,所以我们计算所有的 \(D_i \bmod (A+B)\) ,然后将得到的结果取最小值和最大值,令 \(l = \min\limits_{i = 1} ^ N\{D_i \bmod (A+B)\}\)\(r = \max\limits_{i = 1} ^ N\{D_i \bmod (A+B)\}\),则如果 \(r-l+1 \le A\) 即可。因为我们可以将 \(l\) 对应的那一天视为第一天假期,则 \(r\) 对应的部分一定不会超过 \(A\)

这是我一开始的做法,后来发现 WA 了一个点,考虑下面这个样例:

4 4 5
1 2 8 9

这个样例应该输出 Yes,原因是可以将 1 对应第三天假期,这样,在新的星期中,89 对应的就是假期的第一天和第二天。

所以我们可以发现,第一天假期不一定是 \(l\) 对应的那一天,在上面这个例子中,将 \(8\) 视为假期的第一天,此时序列相当于 8 9 10 11,此时判断的就是 118 的长度。

因此处理出所有的 \(D_i \bmod (A+B)\) ,然后排序,接着枚举哪一天应该成为假期的第一天,假设枚举到 \(i\),这上一天就是 \(i-1\), 判断两者的长度,只要有一个满足即可。

参考代码
#include <iostream>
#include <cstdio>
#include <algorithm>
#include <vector>

using namespace std;

int main()
{
    int n, a, b, c;
    cin >> n >> a >> b;
    c = a + b;
    vector<int> d(n);
    for(int i = 0; i < n; i++)
    {
        scanf("%d", &d[i]);
        d[i] %= c;
    }
    sort(d.begin(), d.end());
    d.erase(unique(d.begin(), d.end()), d.end());
    n = d.size();
    if(d[n-1] - d[0] < a)
    {
        puts("Yes");
        return 0;
    }
    for(int i = 0; i < n - 1; i++)
    {
        if(d[i] + c - d[i+1] < a)
        {
            puts("Yes");
            return 0;
        }
    }
    puts("No");
    return 0;
}
import java.util.Arrays;
import java.util.Scanner;

public class Main {

    public static void main(String[] args) {
        Scanner in = new Scanner(System.in);
        int n = in.nextInt();
        int a = in.nextInt();
        int b = in.nextInt();
        int c = a + b;
        int[] d = new int[n];
        for (int i = 0; i < n; i++) {
            d[i] = in.nextInt();
            d[i] %= c;
        }
        Arrays.sort(d);
        if (d[n - 1] - d[0] < a) {
            System.out.println("Yes");
            return;
        }
        for (int i = 0; i < n - 1; i++) {
            if (d[i] + c - d[i + 1] < a) {
                System.out.println("Yes");
                return;
            }
        }
        System.out.println("No");
        in.close();
    }
}

D - Popcount and XOR

题目大意

给你三个数字 \(a(a \le 60)\)\(b(b \le 60)\)\(C(C \le 2^{60})\),问:是否存在数对 \((X, Y)\) 满足下列所有条件。

  • \(0 \le X < 2^{60}\)
  • \(0 \le Y < 2^{60}\)
  • \(X\) 的二进制中有 \(a\)\(1\)
  • \(Y\) 的二进制中有 \(b\)\(1\)
  • \(X \oplus Y = C\)
解题思路

先将 \(C\) 对应的二进制数组处理出来,然后定义 \(dp[i][x][y]\) 表示是否存在满足 \(C\) 的前 \(i\) 位的前提下,\(X\) 用了 \(x\)\(1\)\(Y\) 用了 \(y\)\(1\) 的方案。则初始时 \(dp[0][0][0] = 1\),转移的时候根据 \(C\) 的数位分类,如果 \(C_i=1\)\(X_iY_i = 10\) 或者 \(X_iY_i = 01\),如果 \(C_i = 0\)\(X_iY_i = 00\) 或者 \(X_iY_i = 11\)

需要注意的是不一定要恰好用完所有的 \(1\),因为 \(C\) 是有可能存在前导 \(0\) 的,而前导 \(0\) 可以由两个 \(1\) 异或得到。

参考代码
#include <iostream>
#include <cstdio>

using namespace std;

typedef long long LL;
const int maxn =  64;
LL a, b, C;
int n, num[maxn];
bool dp[maxn][maxn][maxn];

int main()
{
    cin >> a >> b >> C;
    while(C)
    {
        num[++n] = C & 1;
        C >>= 1;
    }
    dp[0][0][0] = 1;
    for(int i = 1; i <= n; i++)
        if(num[i])
        {
            for(int x = 0; x <= a; x++)
                for(int y = 0; y <= b; y++)
                    dp[i][x][y] = (x && dp[i-1][x-1][y]) || (y && dp[i-1][x][y-1]);
        }
        else
        {
            for(int x = 0; x <= a; x++)
                for(int y = 0; y <= b; y++)
                    dp[i][x][y] = dp[i-1][x][y] || (x && y && dp[i-1][x-1][y-1]);
        }
    int x, y, cnt = -1;
    for(int i = 0; i <= a; i++)
        for(int j = 0; j <= b; j++)
            if(dp[n][i][j] && a-i == b-j && a-i+n <= 60)
            {
                cnt = a-i;
                x = i;
                y = j;
                break;
            }
    if(cnt == -1)
    {
        puts("-1");
        return 0;
    }
    LL X = 0, Y = 0;
    for(int i = n; i; i--)
        if(num[i])
        {
            if(x && dp[i-1][x-1][y])
            {
                X |= 1ll << (i-1);
                x--;
            }
            else
            {
                Y |= 1ll << (i-1);
                y--;
            }
        }
        else
        {
            if(x && y && dp[i-1][x-1][y-1])
            {
                X |= 1ll << (i-1);
                Y |= 1ll << (i-1);
                x--;
                y--;
            }
        }
    for(int i = 0; i < cnt; i++)
    {
        X |= 1ll << (n + i);
        Y |= 1ll << (n + i);
    }
    cout << X << ' ' << Y;
    return 0;
}
import java.util.Scanner;

public class Main {

    public static void main(String[] args) {
        Scanner in = new Scanner(System.in);
        int a = in.nextInt();
        int b = in.nextInt();
        long C = in.nextLong();
        boolean[] num = new boolean[64];
        int n = 0;
        while (C > 0) {
            num[++n] = (C & 1) > 0 ? true : false;
            C >>= 1;
        }
        boolean[][][] dp = new boolean[n + 1][(int) a + 1][(int) b + 1];
        dp[0][0][0] = true;
        for (int i = 1; i <= n; i++)
            if (num[i]) {
                for (int x = 0; x <= a; x++)
                    for (int y = 0; y <= b; y++)
                        dp[i][x][y] = (x > 0 && dp[i - 1][x - 1][y]) || (y > 0 && dp[i - 1][x][y - 1]);
            } else {
                for (int x = 0; x <= a; x++)
                    for (int y = 0; y <= b; y++)
                        dp[i][x][y] = dp[i - 1][x][y] || (x > 0 && y > 0 && dp[i - 1][x - 1][y - 1]);
            }
        int x = 0, y = 0, cnt = -1;
        for (int i = 0; i <= a; i++)
            for (int j = 0; j <= b; j++)
                if (dp[n][i][j] && a - i == b - j && a - i + n <= 60) {
                    cnt = a - i;
                    x = i;
                    y = j;
                    break;
                }
        if (cnt == -1) {
            System.out.println(-1);
            return;
        }
        long X = 0, Y = 0;
        for (int i = n; i > 0; i--)
            if (num[i]) {
                if (x > 0 && dp[i - 1][x - 1][y]) {
                    X |= 1l << (i - 1);
                    x--;
                } else {
                    Y |= 1l << (i - 1);
                    y--;
                }
            } else {
                if (x > 0 && y > 0 && dp[i - 1][x - 1][y - 1]) {
                    X |= 1l << (i - 1);
                    Y |= 1l << (i - 1);
                    x--;
                    y--;
                }
            }
        for (int i = 0; i < cnt; i++) {
            X |= 1l << (n + i);
            Y |= 1l << (n + i);
        }
        System.out.printf("%d %d", X, Y);
        in.close();
    }
}

E - Set Add Query

题目大意

你需要维护一个长度为 \(N(N \le 2 \times 10^5)\) 的序列 \(A = (A_1, A_2, \cdots, A_N)\) 和一个集合 \(S\) 。 初始的时候,\(A\) 中的所有数字都是 \(0\),集合 \(S\) 也为空。有 \(Q(Q \le 2 \times 10^5)\) 个操作,每个操作给你一个数字 \(x_i(1 \le x_i \le N)\),然后执行:

    • 如果 \(x_i\)\(S\) 中,则将其从 \(S\) 中删去。
    • 如果 \(x_i\) 不在 \(S\) 中,则将其加入到 \(S\) 中。
  1. 遍历 \(S\) 中所有的数字 \(x\),然后给 \(A_x\) 加上集合 \(S\) 的大小 \(|S|\)

问:在 \(Q\) 次操作后,序列 \(A\) 的内容是?

解题思路

考虑追踪每个数字在集合中存活的时间,当数字 \(x\) 加入集合,一直到这个数字被删去,中间 \(|S|\) 都会对 \(A_x\) 产生影响,而这个区间是连续的。显然要维护一个 \(|S|\) 变化的前缀和,然后记录一下数字上一次加入的时间,用 last[x] 表示 x 上一次加入集合的时间,如果 last[x] == 0 表示 x 不在集合中。当元素被删去时,查找出对应的前缀和,一次性加上去就好。这样每次操作的时间复杂度就是 \(O(1)\) 的。

\(Q\) 次操作后,还需要对集合中剩下的所有元素再加一遍。

参考代码
#include <iostream>
#include <cstdio>

using namespace std;

typedef long long LL;
const int maxn = 2E5 + 10;
int last[maxn];
LL s[maxn], a[maxn];

LL getsum(int l, int r)
{return s[r] - s[l-1];}

int main()
{
    int n, q, x, now = 0;
    cin >> n >> q;
    for(int i = 1; i <= q; i++)
    {
        scanf("%d", &x);
        if(last[x])
        {
            a[x] += getsum(last[x], i-1);
            last[x] = 0;
            now--;
        }
        else
        {
            last[x] = i;
            now++;
        }
        s[i] = s[i-1] + now;
    }
    for(int i = 1; i <= n; i++)
        if(last[i])
            a[i] += getsum(last[i], q);
    for(int i = 1; i <= n; i++)
        printf("%lld ", a[i]);
    return 0;
}
import java.util.Scanner;

public class Main {
    private static long[] s;

    private static long getsum(int l, int r) {
        return s[r] - s[l - 1];
    }

    public static void main(String[] args) {
        Scanner in = new Scanner(System.in);
        int n = in.nextInt();
        int q = in.nextInt();
        int[] last = new int[n + 1];
        long[] a = new long[n + 1];
        s = new long[q + 1];
        int now = 0;
        for (int i = 1; i <= q; i++) {
            int x = in.nextInt();
            if (last[x] > 0) {
                a[x] += getsum(last[x], i - 1);
                last[x] = 0;
                now--;
            } else {
                last[x] = i;
                now++;
            }
            s[i] = s[i - 1] + now;
        }
        for (int i = 1; i <= n; i++)
            if (last[i] > 0)
                a[i] += getsum(last[i], q);
        for (int i = 1; i <= n; i++)
            System.out.printf("%d ", a[i]);
        in.close();
    }
}


最后更新: 2024-05-22
创建日期: 2024-04-02