2020 CCPC-Wannafly Winter Camp Day1 總結

部分題目列表:

題號 題目 算法
B題 密碼學 簽到題,字符串處理
H題 最大公約數 數學、gcd、大數
F題 乘法 較爲巧妙的使用二分
A題 期望逆序對 -
C題 染色圖 -
I題 K小數查詢 在線線段樹套權值線段樹

B題 密碼學

簽到題,就是一個字符串的計算,可恥的WA了一發是因爲忘記了解碼要倒着去解(iimm00)

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

const int maxn = 1005;
int a[maxn], b[maxn];
string str[maxn];
int s[maxn][105];

void dde(int k)
{
    for (int i = 0; i < str[k].size(); i++) {
        if (str[k][i] > 'Z')
            s[k][i] = str[k][i] - 'a';
        else
            s[k][i] = str[k][i] - 'A' + 26;
    }
}

int main(void)
{
    int n, m;
    scanf("%d%d", &n, &m);
    for (int i = 0; i < m; i++)
        scanf("%d%d", &a[i], &b[i]);
    for (int i = 1; i <= n; i++)
        cin >> str[i];
    
    for (int i = m - 1; i >= 0; i--) {
        int x = a[i], y = b[i];
        string aa = str[x], bb = str[y];
        while (str[x].size() < str[y].size())
            str[x] = str[x] + str[x];
 
        for (int j = 0; j < str[y].size(); j++) {
            dde(x), dde(y);
            int kkk = s[y][j] - s[x][j];
            kkk = (kkk + 52) % 52;
            if (kkk < 26)
                bb[j] = kkk + 'a';
            else
                bb[j] = kkk - 26 + 'A';
        }

        str[x] = aa;
        str[y] = bb;
    }

    for (int i = 1; i <= n; i++)
            cout << str[i] << endl;
    return 0;
}

H題 最大公約數

Java大數走起

題幹:有三個人 A,B,CA,B,C,其中 AABB 共享了一個神祕的數字 kk,已知 1kn1≤k≤n, 現在 AACC 說:“kk 的值等於 xx”。
CC 不太信任 AA,於是想向 BB 確認一下 kk 是否真的等於 xxBB 雖然不想直接把 kk 的值告訴 CC,但是 BB 允許 CC 給出一個正整數 yy(注意 yy 可以大於 nn),然後 BB 會回答 gcd(k,y)gcd(k,y)
現在給出 k,nk,n,你需要幫助 CC 決定這樣的 yy 的取值,使得 CC 一定可以通過 BB 的回答來判斷 AA 有沒有撒謊。如果這樣的 yy 有多個,你需要輸出最小的那個。

輸入第一行是一個整數 T(1T50)T(1≤T≤50)
對於每組數據,輸入一行兩個整數 n,k(1kn500)n,k(1≤k≤n≤500)

對於每組數據,輸出一行一個整數,表示答案。如果滿足條件的 yy 不存在,則輸出 −1。

// package Main;
import java.util.*;
import java.math.*;
public class Main {
    static BigInteger arr[] = new BigInteger[505];
    static BigInteger m(int a) {
        return new BigInteger(a + "");
    }
    static boolean isP(int x) {
        for (int i = 2; i * i <= x; i++) 
            if (x % i == 0) 
                return false;
        return true;
    }
    static void init() {
        arr[1] = BigInteger.ONE;
        for (int i = 2; i <= 500; i++) {
            if (isP(i)) 
                arr[i] = arr[i - 1].divide(arr[i - 1].gcd(m(i))).multiply(m(i));
            else 
                arr[i] = arr[i - 1];
        }
    }
    public static void main(String[] args) {
        init();
        Scanner sc = new Scanner(System.in);
        int t = sc.nextInt();
        while (t-- != 0) {
            int n = sc.nextInt();
            int k = sc.nextInt();
            BigInteger ans = arr[n / k].multiply(m(k));
            System.out.println(ans);
        }
    }
}

F題 乘法

題意:給出一個長度爲 nn 的數列和一個長度爲 mm 的數列, 可以構造得到一個 n×mn×m 的矩陣 CC,其中 Ci,j=Ai×BjC_{i,j}=A_{i}×B_{j}, 給出整數 KK,你需要求出 CC 中第 KK 大的數的值。(1n,m1051≤n,m≤10^{5}, 1Kn×m1≤K≤n×m)

思路:二分的找,需要額外注意當數爲負數和零時候的情況

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

#define IO ios::sync_with_stdio(false), cin.tie(0), cout.tie(0)
typedef long long ll;
const int maxn = 1e5 + 5;

ll ef = 1e12;
ll n, m, k;
vector<ll> a, b;

ll update_a(ll x, ll mid, ll l, ll r)
{
    if (l == r)
        return l;
    int m = (l + r + 1) >> 1;
    if (x * b[m] <= mid)
        return update_a(x, mid, m, r);
    else
        return update_a(x, mid, l, m - 1);
}

ll update_b(ll x, ll mid, ll l, ll r)
{
    if (l == r)
        return l;
    int m = (l + r) >> 1;
    if (x * b[m] <= mid)
        return update_b(x, mid, l, m);
    else
        return update_b(x, mid, m + 1, r);
}

ll Binary_Search(ll l, ll r) 
{
    if (l == r)
        return l;
    ll mid = (l + r) >> 1;

    ll ans = 0;
    for (int i = 0; i < a.size(); i++) {
        if (a[i] < 0)
            ans += m - update_b(a[i], mid, 0, b.size());
        else
            ans += update_a(a[i], mid, -1, b.size() - 1) + 1;
    }

    if (ans >= k)
        return Binary_Search(l, mid);
    else
        return Binary_Search(mid + 1, r);
}

int main(void)
{
    IO;
    cin >> n >> m >> k;
    k = n * m - k + 1;

    ll tmp;
    for (int i = 0; i < n; i++) {
        cin >> tmp;
        a.push_back(tmp);
    }
    for (int i = 0; i < m; i++) {
        cin >> tmp;
        b.push_back(tmp);
    }
    sort(a.begin(), a.end());
    sort(b.begin(), b.end());

    cout << Binary_Search(-ef, ef) << endl;

    return 0;
}

A題 期望逆序對

題意:生成一個每個數aia_{i}是從 [li,ri][l_{i}, r_{i}] 中生成的整數,aa 中不存在相同的數字,求逆序對個數的期望的最小值, 數組範圍至5e35e3.
輸出一行一個整數,表示答案對 998244353 取模後的值。假設答案的最簡分數表示是 ab\frac{a}{b}, 你需要輸出一個整數 kk 滿足 x×yx  mod  998244353x \times y \equiv x \; mod \;998244353

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

const int mod = 998244353;
const int maxn = 5050;

struct section {
    int l, r;
    void read() {
        scanf("%d%d", &l, &r);
    }
    bool operator < (const section& s1) {
        return l + r < s1.l + s1.r;
    }
}a[maxn];

int n, b[maxn];

int quick(int x, int y)
{
    int z = 1;
    while (y) {
        if (y & 1)
            z = 1ll * z * x % mod;
        y >>= 1;
        x = 1ll * x * x % mod;
    }
    return z;
}

int cal(section x, section y)
{
    int l = max(x.l, y.l);
    if (l > x.r)
        return 0;
    int r = min(x.r, y.r);
    int ans = 1ll * (l - y.l + r - y.l) * (r - l + 1) / 2 % mod;
    ans = (ans + 1ll * (x.r - r) * (y.r - y.l + 1)) % mod;
    return ans;
}

int main(void)
{
    scanf("%d", &n);
    for (int i = 1; i <= n; i++)
        a[i].read();
    sort(a + 1, a + n + 1);

    int ans = 0;
    for (int i = 1; i <= n; i++)
        b[i] = quick(a[i].r - a[i].l + 1, mod - 2);

    for (int i = 1; i <= n; i++)
        for (int j = i + 1; j <= n; j++)
            ans = (ans + 1ll * b[i] * b[j] % mod * cal(a[i], a[j])) % mod;

    printf("%d\n", ans);
    return 0;
}

C題 染色圖

—待補—

I題 K小數查詢

樹套樹之線段樹套權值線段樹,在線維護

—待補—

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章