20241102

forgotmyhandle發表於2024-11-04

T1

路徑

注意到顏色出現的順序並不重要,於是考慮狀壓,設 \(f_{x, S}\) 表示從 \(x\) 開始,經過的顏色集合為 \(S\) 的方案數。外層列舉路徑上經過了幾條路徑,然後列舉點轉移即可。

程式碼
#include <iostream>
#define int long long
using namespace std;
int n, m, K;
int clr[300005];
int head[300005], nxt[600005], to[600005], ecnt;
inline void add(int u, int v) { to[++ecnt] = v, nxt[ecnt] = head[u], head[u] = ecnt; }
int f[300005][40];
signed main() {
    // freopen("A.in", "r", stdin);
    // freopen("A.out", "w", stdout);
    cin >> n >> m >> K;
    for (int i = 1; i <= n; i++) cin >> clr[i], f[i][1 << (clr[i] - 1)] = 1;
    for (int i = 1; i <= m; i++) {
        int u, v;
        cin >> u >> v;
        add(u, v);
        add(v, u);
    }
    int ans = 0;
    for (int i = 2; i <= K; i++) {
        for (int j = 1; j < (1 << K); j++) {
            if (__builtin_popcountll(j) != i) 
                continue;
            for (int k = 1; k <= n; k++) {
                if (!(j & (1 << (clr[k] - 1)))) 
                    continue;
                for (int a = head[k]; a; a = nxt[a]) {
                    int v = to[a];
                    f[k][j] += f[v][j ^ (1 << (clr[k] - 1))];
                }
                ans += f[k][j];
            }
        }
    }
    cout << ans << "\n";
    return 0;
}

T2

二分的代價

首先容易想到一個區間 dp,\(dp_{l, r}\) 表示搞定這個區間的最小代價,直接做是三次方。但是發現這個 dp 單調,而且值域很小,只有 \(160\) 不到,於是考慮互換值域和定義域,變成 \(f_{l, x}\) 表示用 \(x\) 的代價,從 \(l\) 開始最多能做到什麼地方。這個地方直接做是平方乘 \(160\),但是發現本質不同的轉移只有 \(9\) 種,而且這個 dp 也是單調的,於是可以透過列舉轉移點的代價簡單做到 \(9 \times 160 \times n\)。就能過了。

程式碼
#include <iostream>
using namespace std;
string str;
int n;
int dp[105][105];
int f[100005][165];
int X = 160;
int x[100005][11];
int main() {
    freopen("cost.in", "r", stdin);
    freopen("cost.out", "w", stdout);
    cin >> str;
    n = str.size();
    str = ' ' + str;
    for (int i = 1; i <= n; i++) {
        for (int j = 1; j <= 9; j++) x[i][j] = x[i - 1][j];
        x[i][str[i] - '0'] = i;
    }
    for (int i = 0; i <= X; i++) f[n + 1][i] = n + 1;
    for (int l = n; l; l--) {
        f[l][0] = l;
        for (int k = 1; k <= X; k++) {
            f[l][k] = f[l][k - 1];
            for (int c = 1; c <= 9; c++) {
                int r = x[f[l][k - c]][c];
                f[l][k] = max(f[l][k], f[r + 1][k - c]);
            }
        }
    }
    for (int i = 0; i <= X; i++) {
        if (f[1][i] >= n + 1) {
            cout << i << "\n";
            break;
        }
    }
    return 0;
}

T3

放假

字串匹配,先考慮 AC 自動機。考慮一個雙向無限序列在 AC 自動機上的形態,發現一定是一條以環開始,以環結束的路徑。由於不能出現給定串,則不能經過所有匹配狀態。如果要不是 \(-1\) 的話,首先不能有任何兩個環交於點,其次不能存在一條路徑經過三個環。這些都強連通縮點之後隨便判一下。在不是 \(-1\) 的情況下,答案即為環的個數 加上 以環開始,以另一個環結束的路徑數量。這個在縮點後的 DAG 上直接數即可。

程式碼
#include <iostream>
#include <string.h>
#include <vector>
#include <queue>
#define int long long
using namespace std;
bool bg;
int K, n;
int son[2000005][10], ncnt1;
bool ep[2000005];
int rt;
void Insert(string str) {
    int p = rt;
    for (int i = 0; i < (int)str.size(); i++) {
        int& t = son[p][str[i] - 'a'];
        p = (!t ? (t = ++ncnt1) : t);
    }
    ep[p] = 1;
}
queue<int> q;
int fail[2000005];
void getfail() {
    for (int i = 0; i < K; i++) {
        if (son[rt][i]) 
            fail[son[rt][i]] = 0, q.push(son[rt][i]);
    }
    while (!q.empty()) {
        int p = q.front();
        q.pop();
        for (int i = 0; i < K; i++) {
            if (son[p][i]) 
                fail[son[p][i]] = son[fail[p]][i], ep[son[p][i]] |= ep[son[fail[p]][i]], q.push(son[p][i]);
            else 
                son[p][i] = son[fail[p]][i];
        }
    }
    // for (int i = 0; i <= ncnt1; i++) ep[i] |= ep[fail[i]];
}
vector<int> G[110005], G2[110005], G3[110005];
int clr[110005], ine[110005], ccnt;
int csz[110005];
string s;
int dfn[110005], low[110005], ncnt;
int stk[110005], sz;
bool vis[110005];
void tarjan(int x) {
    stk[++sz] = x;
    dfn[x] = low[x] = ++ncnt;
    vis[x] = 1;
    for (auto v : G[x]) {
        if (!dfn[v]) {
            tarjan(v);
            low[x] = min(low[x], low[v]);
        } else if (vis[v]) 
            low[x] = min(low[x], dfn[v]);
    }
    if (low[x] == dfn[x]) {
        ++ccnt;
        int t;
        do csz[clr[t = stk[sz--]] = ccnt]++, vis[t] = 0;
        while (t != x);
    }
}
bool isl[110005];
bool tol[110005], frl[110005];
void dfs2(int x) {
    vis[x] = 1;
    for (auto v : G2[x]) {
        if (!vis[v]) 
            dfs2(v);
        tol[x] |= (tol[v] | ine[v]);
    }
}
void dfs3(int x) {
    vis[x] = 1;
    for (auto v : G3[x]) {
        if (!vis[v]) 
            dfs3(v);
        frl[x] |= (frl[v] | ine[v]);
    }
}
int val[110005];
void dfs4(int x) {
    vis[x] = 1;
    for (auto v : G2[x]) {
        if (!vis[v]) 
            dfs4(v);
        val[x] += val[v];
    }
}
string str[10005];
bool ed;
signed main() {
    // cerr << (&ed - &bg) / 1024.0 / 1024.0 << "\n";
    cin >> K >> n;
    if (!n) 
        return 0 * puts(K == 1 ? "1" : "-1");
    for (int i = 1; i <= n; i++) cin >> str[i], Insert(str[i]);
    getfail();
    for (int i = 0; i <= ncnt1; i++) {
        if (ep[i]) 
            continue;
        for (int j = 0; j < K; j++) {
            if (!ep[son[i][j]]) 
                G[i].emplace_back(son[i][j]);
        }
    }
    for (int i = 0; i <= ncnt1; i++) {
        if (!dfn[i]) 
            tarjan(i);
    }
    for (int i = 0; i <= ncnt1; i++) {
        for (auto v : G[i]) {
            if (clr[v] == clr[i]) 
                ine[clr[v]]++;
            else {
                G2[clr[i]].emplace_back(clr[v]);
                G3[clr[v]].emplace_back(clr[i]);
            }
        }
    }
    for (int i = 1; i <= ccnt; i++) {
        if (csz[i] != 1 && csz[i] != ine[i]) 
            return 0 * puts("-1");
    }
    for (int i = 1; i <= ccnt; i++) (!vis[i]) ? dfs2(i) : void();
    memset(vis, 0, sizeof vis);
    for (int i = 1; i <= ccnt; i++) (!vis[i]) ? dfs3(i) : void();
    for (int i = 1; i <= ccnt; i++) {
        if (ine[i] && frl[i] && tol[i]) 
            return 0 * puts("-1");
    }   
    for (int i = 1; i <= ccnt; i++) val[i] = (ine[i] != 0);
    int ans = 0;
    memset(vis, 0, sizeof vis);
    for (int i = 1; i <= ccnt; i++) {
        if (ine[i]) 
            dfs4(i), ans += val[i];
    }
    cout << ans << "\n";
    return 0;
}

T4

普及組題

普及組題都不會做了。