codeforces 1093 題解

12.18 update:補充了 $ F $ 題的題解ios

A 題:c++

題目保證必定有解,就能夠考慮用 $ 2 $ 和 $ 3 $ 來湊出這個數 $ n $spa

若是 $ n $ 是偶數,咱們用 $ n / 2 $ 個 $ 2 $ 來湊出 $ n $ 便可code

若是 $ n $ 是奇數,就用 $ n / 2 - 1 $ 個 $ 2 $ 和 $ 1 $ 個 $ 3 $ 湊出 $ n $ 便可字符串

因此只需輸出 $ n / 2 $get

B 題:it

若是一個字符串重排後必定是迴文串,說明這個字符串只有 $ 1 $ 種字符io

若是有兩種不一樣字符,就能夠把一個放在開頭,一個放在結尾,這樣造成的必定不是迴文串class

一個簡單一點的寫法是 $ sort $ 一下這個字符串,判斷迴文date

C 題:

貪心的想,咱們若是要使整個序列非降,那麼前面的數字要儘可能小,後面的數字要儘可能大

首先 $ a[1] = 0,a[n] = b[1] $,而後貪心的掃過去,在知足條件的狀況下使得前面的數字儘可能小,後面的數字儘可能大便可

D 題:

發現每條邊的兩個端點的數字的奇偶性必定不一樣

因此咱們咱們只要作一次 $ bfs $ 染色並判斷是否能完成染色便可

假設黑點有 $ a $ 個,白點有 $ b $ 個

若是黑點是奇數,方案數是 $ 2^a $ 種,若是白點是奇數,方案數是 $ 2^b $ 種,總方案數是 $ 2^a + 2^b $ 種

而後發現整個圖不必定聯通

因此咱們對每一個聯通塊作一次 $ bfs $ 染色,而後把答案相乘便可

注意不能用 memset,否則 $ T $ 組數據每次 memset 一次確定涼

E 題:

用 $ pa[i] $ 表示 $ i $ 這個數在第一個排列中出現的位置,$ pb[i] $ 表示 $ i $ 這個數在第二個排列中出現的位置

假設查詢區間爲 $ l1,r1,l2,r2 $

若是 $ i $ 這個點對答案形成了貢獻,那麼 $ l1 \le pa[i] \le r1 $ && $ l2 \le pb[i] \le r2 $

容易發現問題變成了二維數點問題,$ cdq $ 分治離線統計答案便可

F 題:

咕咕咕

補鍋完畢

發現 $ len $ 的大小很是大,不能放進狀態裏,又發現 $ k \le 100 $,因此用 $ f[i][j] $ 表示第 $ i $ 個數是 $ j $ 且知足題目所述條件的方案數

發現不能很好的進行轉移,因此再用 $ s[i] $ 表示 $ \sum_{j = 1}^{k} f[i][j] $,而後就是容斥轉移一下

注意一下須要容斥的條件

能夠根據代碼理解一下

#include <bits/stdc++.h>
#define CIOS ios::sync_with_stdio(false);
#define rep(i, a, b) for(register int i = a; i <= b; i++)
#define per(i, a, b) for(register int i = a; i >= b; i--)
#define DEBUG(x) cerr << "DEBUG" << x << " >>> ";
using namespace std;

typedef unsigned long long ull;
typedef long long ll;

template <typename T>
inline void read(T &f) {
    f = 0; T fu = 1; char c = getchar();
    while (c < '0' || c > '9') { if (c == '-') fu = -1; c = getchar(); }
    while (c >= '0' && c <= '9') { f = (f << 3) + (f << 1) + (c & 15); c = getchar(); }
    f *= fu;
}

template <typename T>
void print(T x) {
    if (x < 0) putchar('-'), x = -x;
    if (x < 10) putchar(x + 48);
    else print(x / 10), putchar(x % 10 + 48);
}

template <typename T>
void print(T x, char t) {
    print(x); putchar(t);
}

const int N = 1e5 + 5, md = 998244353;

inline int add(int x, int y) {
    x += y;
    if(x >= md) x -= md;
    return x;
}

inline int sub(int x, int y) {
    x -= y;
    if(x < 0) x += md;
    return x;
}

int f[N][105], cnt[N][105], s[N], a[N];
int n, k, len;

int main() {
    read(n); read(k); read(len); if(len == 1) { cout << 0 << endl; return 0; }
    for(register int i = 1; i <= n; i++) read(a[i]);
    for(register int i = 1; i <= n; i++) {
        for(register int j = 1; j <= k; j++) cnt[i][j] = cnt[i - 1][j] + (a[i] == -1 || a[i] == j);
    }
    s[0] = 1; if(a[1] == -1) { for(register int i = 1; i <= k; i++) f[1][i] = 1; s[1] = k; } else f[1][a[1]] = 1, s[1] = 1;
    for(register int i = 2; i <= n; i++) {
        for(register int j = 1; j <= k; j++) {
            if(~a[i] && a[i] != j) continue;
            f[i][j] = s[i - 1];
            if(i >= len) {
                int l = i - len;
                if(cnt[i][j] - cnt[l][j] == len) {
                    f[i][j] = sub(f[i][j], sub(s[l], f[l][j]));
                }
            }
            s[i] = add(s[i], f[i][j]);
        }
    }
    cout << s[n] << endl;
    return 0;
}

G 題:

習慣性的把曼哈頓距離的絕對值拆出來,用二進制表示

$ 31 $ 的二進制表示是 $ 11111 $,表示 $ 5 $ 維的一個點的座標加入的正負狀況都爲正(即 $ x[1] - y[1] + x[2] - y[2] + x[3] - y[3] + x[4] - y[4] + x[5] - y[5] $

$ 29 $ 的二進制表示是 $ 11101 $,表示 $ x[1] - y[1] + x[2] - y[2] + x[3] - y[3] - x[4] + y[4] + x[5] - y[5] $ (注意 $ x[4] $ 和 $ y[4] $ 的符號變化

那麼咱們要求的就是 max{f[0] + f[31], f[1] + f[30], f[2] + f[29]...}

用線段樹維護便可

相關文章
相關標籤/搜索