---
# System prepended metadata

title: ICPC Thailand National competition 2024

---

ICPC Thailand National competition 2024
===
比賽鏈結
---
https://codeforces.com/gym/105335/ 

比賽影片
---
{%youtube nioZbhBBZ-U %}

記分板
---
賽中成績 6/14 (如果大哥不黑至少 7/14)
![image](https://hackmd.io/_uploads/S1hFUWkRC.png)


題解
---
### A. Auntie's Magical Cake
---
#### 題目摘要

你有$N$個蛋糕，初始的美味度為0。你能吃一個蛋糕的條件為，它不在邊緣且它左右的蛋糕還未被吃。當蛋糕被吃時，你會獲得那塊蛋糕的美味度，你會向左右的蛋糕增加美味度直到碰到被吃過的蛋糕，並且增加的美味度為與目前被吃掉的蛋糕的距離，在最後你可以獲得剩下蛋糕的美味度，求你能獲得的最大美味度
#### 想法
感性證明不會有被吃掉的蛋糕中間有3個蛋糕，所以就設$dp_i$為做到第$i$個且第$i$個蛋糕有被吃的最大美味度為何就好
:::spoiler 實作
```cpp=
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
 
const int N=1e6+10;
ll dp[N];
 
ll calc(ll x){
    return x*(x+1)>>1;
}
 
int main(){
    int n;
    cin >> n;
    if (n==1||n==2){
        cout << 0 << '\n';
        return 0;
    }
    dp[0]=dp[1]=0;
    for(int i=2;i<n;i++){
        if (i>=2) dp[i]=max(dp[i], dp[i-2]+1+calc(n-i));
        if (i>=3) dp[i]=max(dp[i], dp[i-3]+3+calc(n-i));
    }
    cout << max(dp[n-1], dp[n-2]) << '\n';
}
```
:::

### B. Back in the Day
----
#### 題目摘要

給你一串數字代表按電話的次數，求最短字典序最小的字串

#### 想法

水題，greedy用最小長度構字串

:::spoiler 實作

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

#define rep(a, b, c) for (int a = b; a < c; a++)
#define ll long long
#define pb push_back

string mp[8] = {{"abc"}, {"def"}, {"ghi"}, {"jkl"}, {"mno"}, {"pqrs"}, {"tuv"}, {"wxyz"}};

int main() {
    string s; cin >> s;
    int n = s.size();
    int cnt = 1;
    vector<pair<int, int>> ans;
    rep (i, 1, n) {
        if (s[i] == s[i - 1]) cnt++;
        else {
            ans.pb({s[i - 1] - '2', cnt});
            cnt = 1;
        }
    }
    ans.pb({s[n - 1] - '2', cnt});
    for (auto [a, b] : ans) {
        int res = b % mp[a].size();
        if (res) cout << mp[a][res - 1];
        int hehe = 0;
        b -= res;
        rep (i, 0, b / mp[a].size()) {
            cout << mp[a].back();
        }
    }
}
```
:::


### C. Cattering
---
#### 題目摘要

$N$隻貓對$M$個貓糧做匹配，最大化每隻貓能匹配到貓糧的最小滿足度並構造解

#### 想法

對答案二分搜，check跑maxflow找最大匹配，最後隨便輸出解

:::spoiler 實作
```cpp=
#pragma GCC optimize("Ofast, no-stack-protector")
// #pragma GCC target("popcnt, abm, mmx, avx, arch=skylake")
#include<bits/stdc++.h>
using namespace std;

#define rep(a, b, c) for (int a = b; a < c; a++)
#define ll long long
#define pb push_back
#define all(x) (x).begin(), (x).end()

const int IINF = 1e9 + 7;

struct Dinic {
    struct edge {
        int v, r; ll rc;
    };
    vector<vector<edge>> adj;
    vector<int> dis, it;
    Dinic(int n) : adj(n), dis(n), it(n) {}

    void add_edge(int u, int v, ll c) {
        adj[u].pb({v, adj[v].size(), c});
        adj[v].pb({u, adj[u].size() - 1, 0});
    }

    bool bfs(int s, int t) {
        fill(all(dis), IINF);
        queue<int> q;
        q.push(s);
        dis[s] = 0;
        while (!q.empty()) {
            int u = q.front();
            q.pop();
            for (auto [v, r, rc] : adj[u]) {
                if (dis[v] < IINF || rc == 0) continue;
                dis[v] = dis[u] + 1;
                q.push(v);
            }
        }
        return dis[t] < IINF;
    }

    ll dfs(int u, int t, ll cap) {
        if (u == t || cap == 0) return cap;
        for (int &i = it[u]; i < adj[u].size(); ++i) {
            auto &[v, r, rc] = adj[u][i];
            if (dis[v] != dis[u] + 1) continue;
            ll tmp = dfs(v, t, min(cap, rc));
            if (tmp > 0) {
                rc -= tmp;
                adj[v][r].rc += tmp;
                return tmp;
            }
        }
        return 0;
    }

    ll flow(int s, int t) {
        ll ans = 0, tmp;
        while (bfs(s, t)) {
            fill(all(it), 0);
            while ((tmp = dfs(s, t, IINF)) > 0) {
                ans += tmp;
            }
        }
        return ans;
    }

};

int main() {
    ios_base::sync_with_stdio(false), cin.tie(0);
    int n, m; cin >> n >> m;
    vector a(n, vector<int>(m));
    int l = IINF, r = 0;
    rep (i, 0, n) rep (j, 0, m) {
        cin >> a[i][j];
        l = min(l, a[i][j]);
        r = max(r, a[i][j]);
    }
    r++;
    while (r - l > 1) {
        int mid = l + r >> 1;
        Dinic G(n + m + 2);
        int s = n + m, t = s + 1;
        rep (i, 0, n) G.add_edge(s, i, 1);
        rep (i, 0, m) G.add_edge(n + i, t, 1);
        rep (i, 0, n) rep (j, 0, m) if (a[i][j] >= mid) {
            G.add_edge(i, n + j, 1);
        }
        ll f = G.flow(s, t);
        if (f == n) l = mid;
        else r = mid;
    }
    cout << l << '\n';
    Dinic G(n + m + 2);
    int s = n + m, t = s + 1;
    rep (i, 0, n) G.add_edge(s, i, 1);
    rep (i, 0, m) G.add_edge(n + i, t, 1);
    rep (i, 0, n) rep (j, 0, m) if (a[i][j] >= l) {
        G.add_edge(i, n + j, 1);
    }
    ll f = G.flow(s, t);
    rep (i, 0, n) {
        for (auto [v, r, rc] : G.adj[i]) if (v != s && rc == 0) {
            cout << v - n + 1 << ' ';
            break;
        }
    }
}

```
:::

### D. Disinfection Patch
---
#### 題目摘要
給$N$個清潔劑以及$N$個細菌的座標，找到一組$(S, X, Y)$使得清潔劑$(x, y)$的座標變$(S \times x+X, S \times y+Y)$能夠一一對應到所有細菌點
#### 想法

對清潔劑和細菌座標sort後就能直接$O(N)$判對應點了，$N$不知道為啥給了$N^2$量級，怪題，但我們賽中被騙實作了$N^2 \log N$把清潔劑對每個細菌判斷一次

:::spoiler 實作
```cpp=
#pragma GCC optimize("Ofast, no-stack-protector")
// #pragma GCC target("popcnt, abm, mmx, avx, arch=skylake")
#include<bits/stdc++.h>
using namespace std;

#define rep(a, b, c) for (int a = b; a < c; a++)
#define ll long long
#define pb push_back
#define all(x) (x).begin(), (x).end()
#define pll pair<ll, ll>
#define F first
#define S second

const int IINF = 1e9 + 7;

pll operator + (pll a, pll b) {
    return {a.first + b.first, a.second + b.second};
}

pll operator - (pll a, pll b) {
    return {a.first - b.first, a.second - b.second};
}

int main() {
    ios_base::sync_with_stdio(false), cin.tie(0);
    int n; cin >> n;
    vector<pll> din(n), bac(n);
    for (auto &[x, y] : din) cin >> x >> y;
    for (auto &[x, y] : bac) cin >> x >> y;
    sort(all(din));
    sort(all(bac));
    if (bac[0] == bac.back()) {
        cout << 0 << ' ' << bac[0].F << ' ' << bac[0].S << '\n';
        exit(0);
    }
    vector<pll> vin;
    rep (i, 1, n) {
        vin.pb(din[i] - din[0]);
    }
    auto dis = [&](pll x) -> ll {
        return x.F * x.F + x.S * x.S;
    };
    sort(all(vin));
    rep (i, 0, n) {
        vector<pll> bin;
        rep (j, 0, n) if (i != j) bin.pb(bac[j] - bac[i]);
        sort(all(bin));
        pll z = {0, 0};
        int st = 0;
        while (st < n - 1 && bin[st] == z && vin[st] == z) {
            st++;
        }
        if (bin[st] == z || vin[st] == z) continue;
        if (vin[st].F == 0) {
            if (bin[st].S % vin[st].S != 0) continue;
            ll s = bin[st].S / vin[st].S;
            bool f = 1;
            rep (j, st, n - 1) {
                if (bin[j].F != vin[j].F * s || bin[j].S != vin[j].S * s) f = 0;
            }
            if (f && s >= 0) {
                cout << s << ' ';
                din[st].F *= s;
                din[st].S *= s;
                cout << bac[i].F - din[st].F << ' ' << bac[i].S - din[st].S << '\n';
                exit(0);
            }
            continue;
        }
        if (vin[st].S == 0) {
            if (bin[st].F % vin[st].F != 0) continue;
            ll s = bin[st].F / vin[st].F;
            bool f = 1;
            rep (j, st, n - 1) {
                if (bin[j].F != vin[j].F * s || bin[j].S != vin[j].S * s) f = 0;
            }
            if (f && s >= 0) {
                cout << s << ' ';
                din[st].F *= s;
                din[st].S *= s;
                cout << bac[i].F - din[st].F << ' ' << bac[i].S - din[st].S << '\n';
                exit(0);
            }
            continue;
        }
        if (bin[st].F % vin[st].F != 0 || bin[st].S % vin[st].S != 0) continue;
        ll s = bin[st].F / vin[st].F;
        if (bin[st].S / vin[st].S != s) continue; 
        bool f = 1;
        rep (j, st, n - 1) {
            if (bin[j].F != vin[j].F * s || bin[j].S != vin[j].S * s) f = 0;
        }
        if (f && s >= 0) {
            cout << s << ' ';
            din[0].F *= s;
            din[0].S *= s;
            cout << bac[i].F - din[0].F << ' ' << bac[i].S - din[0].S << '\n';
            exit(0);
        }
    }
    cout << -1 << '\n';
}
```
:::

### E. Executive's Holidays
---
#### 題目摘要

有$n$個人、$t$天，每一天規定要最少要$a_i$個人上班，求最大$\sum\limits_{i = 1}^n$(第$i$人最長連續休假時間)

#### 想法

將$a_i$限制反過來用n去扣掉，變成每天最多能有幾個人休息。

將天數當x軸每天最大休息人數當y軸畫長條圖，可以想到一個很好的greedy作法：
每次將最長的連續一條給一個人，刪掉那行後再做一樣的事。
更進一步的，可以直接想成切成一塊一塊目前最大長度可以到的最高矩形。
注意到每塊矩形的上界至少會對應到一個柱狀圖的頂端，因此矩形數量不會超過T個！

如此一來我們只需要知道每個舉行的長寬，對寬從大到小排序後取到剛好n個高就好，但要如何好好的算出矩形呢？

可以考慮從左往右掃用單調棧來維護，需要維護的是一個高度嚴格遞增的序列，對於同樣高度我們會想去維護最左端的那個。若出現下降的會需要去好好比對現在這個跟棧的前前一個的高度，總之就是把頂端取高的畫一條橫線切出矩形的感覺。

:::spoiler 實作
```cpp=
#pragma GCC optimize("Ofast, no-stack-protector")
// #pragma GCC target("popcnt, abm, mmx, avx, arch=skylake")
#include<bits/stdc++.h>
using namespace std;

#define rep(a, b, c) for (int a = b; a < c; a++)
#define ll long long
#define pb push_back
#define all(x) (x).begin(), (x).end()
#define pii pair<int, int>
#define F first
#define S second

const int IINF = 1e9 + 7;

void solve() {
    int n, t; cin >> t >> n;
    vector<int> a(n + 1);
    rep (i, 0, n) {
        cin >> a[i];
        a[i] = t - a[i];
    }
    a[n] = 0;
    vector<pii> st;
    st.pb({-1, 0});
    vector<pii> rec;
    rep (i, 0, n + 1) {
        if (st.back().S < a[i]) {
            st.pb({i, a[i]});
            continue;
        }
        if (st.back().S == a[i]) continue;
        while (st.back().S > a[i]) {
            if (st.end()[-2].S < a[i]) {
                rec.pb({i - st.back().F, st.back().S - a[i]});
                st.back().S = a[i];
                break;
            } else {
                rec.pb({i - st.back().F, st.back().S - st.end()[-2].S});
            }
            st.pop_back();
        }
        if (st.back().S == a[i]) continue;
        st.pb({i, a[i]});
    }
    sort(all(rec), greater<pii>());
    int need = t;
    ll ans = 0;
    for (auto [w, h] : rec) {
        h = min(h, need);
        ans += 1LL * w * h;
        need -= h;
        if (need == 0) break;
    }
    cout << ans << '\n';
}

int main() {
    ios_base::sync_with_stdio(false), cin.tie(0);
    solve();
}
```
:::

### F. Fill T
---
#### 題目摘要
給你$\,R \times C\,$的矩形，請你用T字形來構造填滿整個矩形
#### 想法
非常好構造實作題，先強制讓$\,R \leq C\,$，輸出時好好的x, y互換就好，中間多用函式避免搞重複咚咚
1. $R < 3$：一看就直接-1
2. $R = 3$：若C < 6就-1，否則如下圖，第二、五列那裡可以無限延長
![image](https://hackmd.io/_uploads/ryLhhmglJg.png)
3. $R = 4$：如下圖，右上左下固定後左右可以無限延長
![image](https://hackmd.io/_uploads/Hy2XaXleyx.png)
4. $R = 5$：如下圖，第三列那裡可以無限延長
![image](https://hackmd.io/_uploads/Hy242Xlekg.png)

5. $R \geq 6$：如下圖，可以在左、右、下畫個T，使得R、C各少2，一直這樣做直到$R<6$套回之前的狀況
![image](https://hackmd.io/_uploads/ryF_2Xex1x.png)


:::spoiler 實作
```cpp=
#pragma GCC optimize("O3,unroll-loops")
#include<bits/stdc++.h>
using namespace std;
#define ZTMYACANESOCUTE ios_base::sync_with_stdio(0), cin.tie(0)
#define ll long long
#define pb push_back
#define all(a) (a).begin(), (a).end()
#define rep(X, a, b) for(int X = a; X < b; ++X)

void solve() {
    int r, c; cin >> r >> c;
    bool f = r > c;
    if (f) swap(r, c);
    if (r < 3) {
        cout << -1 << '\n';
        return;
    } 
    auto out = [&](vector<tuple<int, int, int, int, int, int>> &T) -> void {
        cout << T.size() << '\n';
        for (auto [x, y, u, l, d, r] : T) {
            if (f) {
                swap(x, y);
                swap(u, l);
                swap(d, r);
            }
            cout << x << ' ' << y << ' ' << u << ' ' << l << ' ' << d << ' ' << r << '\n';
        }
    };
    vector<tuple<int, int, int, int, int, int>> T;
    auto r3 = [&](int L, int R) -> void {
        T.pb({L, 2, 1, 0, 1, c - 5});
        T.pb({R - 2, 3, 1, c - 4, 0, 1});
        T.pb({R - 3, 1, 0, c - 5, 1, 2});
        T.pb({R, 2, 1, 1, 1, 0});
    };
    auto r4 = [&](int L, int R) -> void {
        T.pb({L, 2, 1, 0, 1, c - 3});
        T.pb({L + 1, 4, 1, 1, 0, c - 3});
        T.pb({R - 1, 1, 0, c - 3, 1, 1});
        T.pb({R, 3, 1, c - 3, 1, 0});
    };
    auto r5 = [&](int L, int R) -> void {
        T.pb({L + 1, 1, 0, 1, 1, c - 4});
        T.pb({L, 3, 1, 0, 1, c - 3});
        T.pb({L + 1, 5, 1, 1, 0, c - 3});
        T.pb({R - 1, 2, 1, c - 4, 1, 0});
        T.pb({R, 4, 3, c - 3, 1, 0});
    };
    if (r == 3) {
        if (c < 6) {
            cout << -1 << '\n';
            return;
        }
        r3(1, c);
        out(T);
        return;
    }
    if (r == 4) {
        r4(1, c);
        out(T);
        return;
    }
    if (r == 5) {
        r5(1, c);
        out(T);
        return;
    }
    if (r >= 6) {
        int L = 1, R = c, D = r;
        while (r >= 6) {
            T.pb({L, D - 1, D - 2, 0, 1, 1});
            T.pb({L + 2, D, 1, 1, 0, c - 4});
            T.pb({R, D - 1, D - 2, c - 4, 1, 0});
            L++, R--, D -= 2;
            r -= 2, c -= 2;
        }
        if (r == 4) r4(L, R);
        if (r == 5) r5(L, R);
        out(T);
        return;
    }
}
 
int main() {
    ZTMYACANESOCUTE;
    int T = 1;
    cin >> T;
    while (T--) {
        solve();
    }
}
```
:::

### G. Glory Road
---
#### 題目摘要
給三角形的3中點，求三頂點座標
#### 想法
列式看一下就好，注意一下輸出順序
:::spoiler 實作
```cpp=
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
 
const int N=1e6+10;
#define F first
#define S second
typedef pair<int,int> pii;
pii operator-(pii __, pii _){
    return {__.F-_.F,__.S-_.S};
}
pii operator+(pii __, pii _){
    return {__.F+_.F,__.S+_.S};
}
int main(){
    int n;
    pii A, B, C;
    cin >> A.F >> A.S >> B.F >> B.S >> C.F >> C.S;
    pii sum = A+B+C;
    B.F*=2;B.S*=2;
    A.F*=2;A.S*=2;
    C.F*=2;C.S*=2;
    pii a=sum-B;
    pii b=sum-C;
    pii c=sum-A;
    cout << a.F << ' ' << a.S << '\n' << b.F << ' ' << b.S << '\n' << c.F << ' ' << c.S;
}
```
:::


### H. Heavenly Sequence (待補)
---
#### 題目摘要

#### 想法

:::spoiler 實作
```cpp=

```
:::

### I. Ideal Permutation Pairing
---
#### 題目摘要
將長度為$N$的所有排列依字典序連成一個環，給其中一個排列，問它對面的排列是誰
#### 想法
不難發現題目就是要求我走$\frac{N}{2}\times (N-1)!$步後的排列為何，並且當走$(N-1)!$步時，只會將陣列中首項往下一個走，其餘位置的大小關係並不會變，因此只要找到首項在走$N!$後是誰，其餘再用剩餘的數字按照原本陣列的大小關係排就好，這樣能把$N$為偶數的狀況做完。而當$N$是奇數，可以拆成走$\lfloor\frac{N}{2}\rfloor\times(N-1)!+ \frac{N-1}{2}\times (N-2)!$，走$(N-2)!$時只要從找首項變成找頭兩項就好
:::spoiler 實作
```cpp=
#pragma GCC optimize("Ofast, no-stack-protector")
#include<bits/stdc++.h>
using namespace std;
typedef pair<int, int> pii;
#define F first
#define S second

int main(){
    ios::sync_with_stdio(false);
    cin.tie(0);
    cout.tie(0);
    int n;
    cin >> n;
    vector<int> v(n);
    for(int i=0;i<n;i++) cin >> v[i];
    vector<int> rest;
    vector<pii> pos;
    int x=(v[0]+(n/2)-1+n)%n+1;
    for(int i=1;i<=n;i++) if (i!=x) rest.push_back(i);
    for(int i=1;i<n;i++) pos.push_back({v[i], i});
    sort(pos.begin(), pos.end());
    v[0]=x;
    for(int i=0;i<n-1;i++) v[pos[i].S]=rest[i];
    if (n&1){
        auto calc=[&](int x, int y)->pii {
            --x, --y;
            if (y>x) y--;
            y+=(n-1)/2;
            x+=(y/(n-1));
            y%=(n-1);
            if (x>(n-1)||((x==(n-1))&&(y==(n-1)))){
                x-=(n-1);
                y-=(n-1);
                if (y<0){
                    x--;
                    y+=(n-1);
                }
            }
            x++, y++;
            if (y>=x) y++;
            return {x, y};
        };
        auto [d1, d2]=calc(v[0], v[1]);
        rest.clear();
        for(int i=1;i<=n;i++) if (i!=d1&&i!=d2) rest.push_back(i);
        pos.clear();
        for(int i=2;i<n;i++) pos.push_back({v[i], i});
        sort(pos.begin(), pos.end());
        v[0]=d1, v[1]=d2;
        for(int i=0;i<n-2;i++) v[pos[i].S]=rest[i];
    }
    for(auto x:v) cout << x << ' ';
    cout << '\n';
}

```
:::


### J. Jewel Collection
---
#### 題目摘要
有$N$種寶石$C$種顏色，每種寶石有價值$v_i$，並且可以顯現自己其中一種顏色，有個富豪想炫富，他不只要收集所有顏色，還要讓總價值越大越好，問如果能的話，輸出最大總價值並輸出每個顏色應該選哪種寶石，否則輸出-1
#### 想法
注意到每種寶石最多只有兩種顏色 ，~~一看，哇!這不就圖論嗎?~~
把顏色當作點，只有一個顏色的寶石當作自環，兩個顏色的當作一條邊。只要每個點能對應到一條邊，再讓邊權最大就能得到答案，換句話說，就是要求最大生成水母圖，實作時注意自環就好
:::spoiler 實作
```cpp=
#pragma GCC optimize("Ofast, no-stack-protector")
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;

const int N=2e5+10;
vector<pair<int, int>> v[N];
int in[N], res[N];
bool vis[N], used[N];

ll ans=0;
int dsu[N];
bool cyc[N];
int root(int x){return (dsu[x]==x)?x:dsu[x]=root(dsu[x]);}
void merge(int a, int b, ll w, int idx){
    int ra=root(a), rb=root(b);
    if ((cyc[ra]==1&&cyc[rb]==1)) return;
    if (ra==rb){
        if (cyc[ra]) return;
        ans+=w;
        cyc[ra]=1;
        v[a].push_back({b, idx});
        if (a!=b) v[b].push_back({a, idx});
        in[a]++;
        if (a!=b) in[b]++;
    }else{
        ans+=w;
        if (cyc[ra]==0) swap(ra, rb);
        dsu[rb]=ra;
        v[a].push_back({b, idx});
        v[b].push_back({a, idx});
        in[a]++, in[b]++;
    }
}

int main(){
    int n, c;
    cin >> n >> c;
    vector<tuple<int, int, int, int>> edg(n);
    for(int i=1;i<=c;i++) dsu[i]=i, cyc[i]=0;
    for(int i=0;i<n;i++){
        int t;
        cin >> t;
        auto &[c, a, b, idx]=edg[i];
        if (t==1){
            cin >> a;
            b=a;
        }else cin >> a >> b;
        cin >> c;
        idx=i+1;
    }
    sort(edg.begin(), edg.end(), greater<tuple<int, int, int, int>>());   
    for(auto [c, a, b, idx]:edg){
        merge(a, b, c, idx);
    }
    bool phlag=1;
    for(int i=1;i<=c;i++) phlag&=cyc[root(i)];
    if (!phlag){
        cout << -1 << '\n';
        return 0;
    }
    queue<int> q;
    for(int i=1;i<=c;i++) if (in[i]==1) q.push(i), vis[i]=1;
    while(!q.empty()){
        int x=q.front();
        q.pop();
        for(auto [e, idx]:v[x]) if (e==x||!vis[e]){
            if (used[idx]) continue;
            used[idx]=1;
            in[e]--;
            res[x]=idx;
            if (in[e]==1){
                q.push(e);
                vis[e]=1;
            }
        }
    }
    cout << ans << '\n';
    for(int i=1;i<=c;i++) if (!vis[i]){
        int j=i;
        do{
            int nex=-1;
            for(auto [k, idx]:v[j]) if (!vis[k]){
                if (used[idx]) continue;
                used[idx]=1;
                res[j]=idx;
                nex=k;
                vis[k]=1;
                break;
            }
            if (nex==-1) break;
            j=nex;
        }while(j!=i);
    }
    for(int i=1;i<=c;i++) cout << res[i] << ' ';
    cout << '\n';
}

```
:::


### K. Kid Rally
---
#### 題目摘要
給你$N\times M$的座標，每個座標有$S_{i,j}$的值，有兩個人會分別成$(1, 1), (1, M)$開始，他們只能每次往下走一層，並且兩人的路徑不能香蕉，他們可以隨時結束，或是直到走到最底，最大化兩人分別走過的路徑上的和的乘積

#### 想法
~~一看就知道是裸dp，但我被隊友黑了~~。可以設$dp_{i, j}$為走到第$i$列且左邊的人的和為$j$，右邊的人最大和為多少，可以列出轉移式$dp_{i, j}=max(dp_{i-1, j+d}+e)$，$d$和$e$是$0$到$9$，並且$d$的位置在$e$的左邊。然而這題允許一個人停止後另一人繼續走，因此只要存每列最大值的後綴和，在每做完一列的dp後，讓較小和的那人加上後下一列的後綴和就好。複雜度為$O(100N^2)$，壓一下常以免被TLE
:::spoiler 實作
```cpp=
#pragma GCC optimize("Ofast, no-stack-protector")
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
 
int main(){
    ios_base::sync_with_stdio(false), cin.tie(0);
    int n, m;
    cin >> n >> m;
    vector<vector<int>> s(n, vector<int>(m));
    vector<vector<vector<bool>>> lr(n, vector<vector<bool>>(10, vector<bool>(10, 0)));
    vector<int> suf(n+1, 0);
    for(int i=0;i<n;i++) for(int j=0;j<m;j++) cin >> s[i][j];
    int sum=0;
    for(int i=0;i<n;i++){
        vector<bool> pre(10, 0);
        int mx=0;
        for(int j=0;j<m;j++){
            for(int k=0;k<10;k++) if (pre[k]==1){
                lr[i][k][s[i][j]]=1;
            }
            pre[s[i][j]]=1;
            mx=max(mx, s[i][j]);
        }
        suf[i]=mx;
    }
    for(int i=n-1;~i;--i) suf[i]+=suf[i+1];
    ll ans=0;
    vector<int> dp(9001, -9001);
    dp[s[0][0]]=s[0][m-1];
    for(int j=0;j<=9;j++){
        ll a=j, b=dp[j];
        ans=max(ans, (a+suf[1])*b);
        ans=max(ans, a*(b+suf[1]));
    }
    for(int i=2;i<=n;i++) {
        vector<int> tmp(9001, -9001);
        for(int j=0;j<=i*9;j++) {
            for(int x=0;x<10;x++) for(int y=0;y<10;y++) if (lr[i-1][x][y]){
                if (j+x<=i*9) tmp[j+x]=max(tmp[j+x], dp[j]+y);
            }
        }
        dp.swap(tmp);
        for(int j=0;j<=i*9;j++){
            ll a=j, b=dp[j];
            ans=max(ans, (a+suf[i])*b);
            ans=max(ans, a*(b+suf[i]));
        }
    }
    cout << ans << '\n';
}

```
:::

### L. Lulu and Friends
---
#### 題目大綱
給你一個字串$T$，有$Q$筆詢問，每題詢問有一個字串$S$，問你最少需要刪減多少個$T$的字元使$S$成為$T$的子字串，不行輸出-1

#### 想法
注意到$\vert T\vert\le 20$，用位元枚舉就好
:::spoiler 實作
```cpp=
#include<bits/stdc++.h>
using namespace std;
 
map<string, int> mp;
 
int main(){
    ios::sync_with_stdio(false);
    cin.tie(0);
    string s;
    cin >> s;
    int n=s.size();
    for(int i=1;i<(1<<n);i++){
        string tmp;
        int l=-1, r=-1;
        for(int j=0;j<n;j++){
            if (i>>j&1){
                if (l==-1) l=j;
                r=j;
                tmp.push_back(s[j]);
            }
        }
        int res=0;
        for(int j=l;j<r;j++){
            if (~i>>j&1) res++;
        }
        if (mp.find(tmp)==mp.end()){
            mp[tmp]=res;
        }else{
            mp[tmp]=min(mp[tmp], res);
        }
    }
    int q;
    cin >> q;
    while(q--){
        string t;
        cin >> t;
        if (mp.find(t)==mp.end()) cout << -1 << '\n';
        else cout << mp[t] << '\n';
    }
}
```
:::


### M. Marriage Proposals (待補)
---
#### 題目大綱

#### 想法

:::spoiler 實作
```cpp=

```
:::

### N. [N]ew YoRHa Security
---
#### 題目大綱
給定一質數$P$，對於每個$1 \le k \le P-1$求有多少數對$1 \le x, y \le P-1$滿足
* $x ^ {y} \equiv k \mod P$ 
#### 想法
我不會數學QQ，打表通靈出對於一個數字的所有次方，有同樣循環節長度的循環節裡的元素都一樣，觀察到對一個數的次方模一個質數的循環節結尾會是1(再乘一次自己會變自己)，對P-1的所有因數長度看是不是循環節的結尾，直接對最短長度紀錄多少數字，再對每個可能長度對答案陣列加(數字 * ($P - 1$ / 循環節長度))，整體複雜度為$O(P \log P \times d )$，$d$是$P-1$的因數個數，數量最多差不多240
:::spoiler 實作
```cpp=
#pragma GCC optimize("O3,unroll-loops")
#pragma GCC target("avx,popcnt,sse4,abm")
#include<bits/stdc++.h>
using namespace std;
#define ZTMYACANESOCUTE ios_base::sync_with_stdio(0), cin.tie(0)
#define ll long long
#define pb push_back
#define all(a) (a).begin(), (a).end()
#define rep(X, a, b) for(int X = a; X < b; ++X)

void solve() {
    int p; cin >> p;
    vector<int> d;
    for (int i = 1; i * i <= p - 1; ++i) if ((p - 1) % i == 0) {
        d.pb(i);
        if (i != (p - 1) / i) d.pb((p - 1) / i);
    }
    vector<vector<int>> len(p);
    sort(all(d));
    rep (i, 1, p) for (int x : d) {
        if (fpow(i, x, p) == 1) {
            len[x].pb(i);
            break;
        }
    }
    vector<ll> ans(p, 0);
    rep (i, 1, p) if (len[i].size()) {
        int sz = len[i].size(), l = (p - 1) / i;
        ll cur = len[i].back();
        rep (j, 0, i) {
            ans[cur] += 1LL * sz * l;
            cur = cur * len[i].back() % p;
        }
    }
    rep (i, 1, p) cout << ans[i] << ' ';
}
 
int main() {
    ZTMYACANESOCUTE;
    int T = 1;
    // cin >> T;
    while (T--) {
        solve();
    }
}
```
:::
