总结

考场梦游,T1,T2 交错了,T1 忘记关注释和 Hash 值了,T2 也忘记关注释了…

T1.回文串

题意:翻转 \([l, r]\) 使序列变为回文串。

1. 考虑 \([l, r]\) 全在左半边或者右半边,举例全在左半边。

这时我们可以通过右半边知道整个序列的样子,而我们只能改变连续一段,那么对比翻转前后不一样的地方,看看翻转一次能不能达到就行了。

2. 考虑 \([l, r]\) 分居两侧。

先说结论,找到一个最大的整数 \(x\),使 \(s[1, x] = s[n, n - x + 1]\),这时我们只能翻转 \([x + 1, y] (y \in [x + 1, n - x])\) 或者 \([y, n - x] (y \in [x + 1, n - x])\)

证明,为什么可以抛弃掉首尾回文的部分。

考虑答案是翻转 \([l, r] (l \leq x)\)

由于首尾回文,那么 \(s[l, x] = s[n - l + 1, n - x + 1]\),由于 \([l, r]\) 翻转后序列为回文,那么 \(len = x - l + 1, s[r - len + 1, r] = s[x, l]\),所以说 \([l, r]\) 首尾长度小于等于 \(x - l + 1\) 的部分回文,因此去掉这一段也有正确答案。

证明,为什么翻转区间必须包含 \(l\) 或者 \(r\)

因为 \(s[l] \neq s[r]\),所以必须要有一个位置被修改。

有了这个结论代码还是比较好写的,指针去找 \(x\),然后然后枚举 \(y\),用 \(Hash\) \(\mathcal{O}(1)\) 判断 序列是否回文。

参考代码

// ubsan: undefined
// accoders
#include <set>
#include <map>
#include <ctime>
#include <cmath>
#include <queue>
#include <stack>
#include <cstdio>
#include <vector>
#include <random>
#include <cstdlib>
#include <cstring>
#include <iostream>
#include <algorithm>
using namespace std;
#define fi first
#define se second
#define db double
#define LL long long
//#define int long long
#define PII pair<int, int>
#define ULL unsigned long long
#define MP(x, y) make_pair(x, y)
#define rep(i, j, k) for (int i = (j); i <= (k); i++)
#define per(i, j, k) for (int i = (j); i >= (k); i--)

template <typename T>
void read(T &x) {
    x = 0;
    T f = 1;
    char ch = getchar();
    while (ch < '0' || ch > '9') {
        if (ch == '-')
            f = -1;
        ch = getchar();
    }
    while (ch >= '0' && ch <= '9') {
        x = (x << 3) + (x << 1) + ch - '0';
        ch = getchar();
    }
    x *= f;
}
template <typename T, typename... Args>
void read(T &x, Args &... Arg) {
    read(x), read(Arg...);
}
const int MaxPrint = 1000;
int Poi_For_Print, Tmp_For_Print[MaxPrint + 5];
template <typename T>
void write(T x) {
    if (x == 0) {
        putchar('0');
        return;
    }
    bool flag = (x < 0 ? 1 : 0);
    x = (x < 0 ? -x : x);
    while (x) Tmp_For_Print[++Poi_For_Print] = x % 10, x /= 10;
    if (flag)
        putchar('-');
    while (Poi_For_Print) putchar(Tmp_For_Print[Poi_For_Print--] + '0');
}
template <typename T, typename... Args>
void write(T x, Args... Arg) {
    write(x);
    putchar(' ');
    write(Arg...);
}
template <typename T, typename... Args>
void print(T x, char ch) {
    write(x);
    putchar(ch);
}
template <typename T>
T Max(T x, T y) {
    return x > y ? x : y;
}
template <typename T>
T Min(T x, T y) {
    return x < y ? x : y;
}
template <typename T>
T Abs(T x) {
    return x > 0 ? x : -x;
}

const int Maxn = 1e5;
const ULL P = 13331;

int t, n;
char s[Maxn + 5], b[Maxn + 5];
ULL pre[Maxn + 5], fall[Maxn + 5], w[Maxn + 5];

signed main() {
    // freopen ("1.in", "r", stdin);
    // freopen ("1.out", "w", stdout);
    freopen("palindrome.in", "r", stdin);
    freopen("palindrome.out", "w", stdout);

    w[0] = 1;
    rep(i, 1, Maxn) w[i] = w[i - 1] * P;
    read(t);
    // printf ("%d\n", t);
    while (t--) {
        read(n);
        scanf("%s", s + 1);

        // print (n, '\n');
        // printf ("%s\n", s + 1);

        bool ck = 1;
        for (int i = 1, j = n; i <= j; i++, j--)
            if (s[i] != s[j])
                ck = 0;
        if (ck) {
            printf("1 1\n");
            continue;
        }
        int li1 = n / 2, li2 = n & 1 ? (n / 2 + 2) : (n / 2 + 1);
        pre[0] = fall[n + 1] = 0;
        rep(i, 1, n) {
            b[n - i + 1] = s[i];
            pre[i] = pre[i - 1] * P + s[i] - 'a' + 1;
        }
        per(i, n, 1) fall[i] = fall[i + 1] * P + s[i] - 'a' + 1;

        // half left part
        int l = 1, r = li1;
        while (l <= n && b[l] == s[l]) l++;
        while (r >= 1 && b[r] == s[r]) r--;
        for (int i = l, j = r; i <= j; i++, j--) swap(b[i], b[j]);
        bool fl = 1;
        rep(i, 1, li1) if (b[i] != s[i]) fl = 0;
        if (fl) {
            print(l, ' '), print(r, '\n');
            continue;
        }

        // half right part
        rep(i, 1, n) b[n - i + 1] = s[i];
        l = li2, r = n;
        while (l <= n && b[l] == s[l]) l++;
        while (r >= 1 && b[r] == s[r]) r--;
        for (int i = l, j = r; i <= j; i++, j--) swap(b[i], b[j]);
        fl = 1;
        rep(i, li2, n) if (b[i] != s[i]) fl = 0;
        if (fl) {
            print(l, ' '), print(r, '\n');
            continue;
        }

        // others
        l = 1, r = n;
        while (l <= r && s[l] == s[r]) l++, r--;
        PII ans = MP(-1, -1);
        // printf ("---%d %d\n", l, r);
        rep(i, l, r) {
            // swap (i, r)
            ULL tmp1 =
                (pre[i - 1] - pre[l - 1] * w[i - l]) * w[r - i + 1] + (fall[i] - fall[r + 1] * w[r - i + 1]);
            ULL tmp2 = (pre[r] - pre[i - 1] * w[r - i + 1]) * w[i - l] + (fall[l] - fall[i] * w[i - l]);
            if (tmp1 == tmp2) {
                ans = MP(i, r);
                break;
            }

            // swap (l, i)
            tmp1 = (fall[l] - fall[i + 1] * w[i - l + 1]) * w[r - i] + (pre[r] - pre[i] * w[r - i]);
            tmp2 =
                (fall[i + 1] - fall[r + 1] * w[r - i]) * w[i - l + 1] + (pre[i] - pre[l - 1] * w[i - l + 1]);
            if (tmp1 == tmp2) {
                ans = MP(l, i);
                break;
            }
        }
        print(ans.fi, ' '), print(ans.se, '\n');
    }
    return 0;
}

T2.国际象棋

不是 \(std\) 的做法

\(n * m\) 的棋盘能摆放多少对互相能吃到的马。

打表容易发现若 \(n, m \geq 3,nm \leq 10000\) 的时候有方式把他铺满(ps:std 就是证明了去掉 \(nm \leq 1000\) 结论也成立),那么考虑通过构造缩小 \(n, m\),使 \(nm \leq 10000\)

如果有 \(n ~ or ~ m \leq 2\),容易得出结论对数最多的方案数长这样。

怎么缩小 \(n, m\)?考虑四格四格缩小,以缩小 \(m\) 为例,判断 \(n\) 的奇偶性。

\(n\) 为奇数:先涂一个 \(3 * 4\),然后涂 \(2 * 4\)
\(n\) 为偶数:涂 \(2 * 4\)

通过手玩或者暴力等方法找到占满 \(3 * 4, 2 * 4\) 的方法。

我比较懒,发现缩小不可能使 \(n ~ or ~ m \leq 2\),因为要满足 \(nm \geq 1e4, n, m \leq 1e3\)。所以只缩小 \(n\) 或者 \(m\) 就行了。

有点卡常,调调参数就过了 /se,如果用 \(n, m\) 都缩的方法,那么可以缩的 \(3 \leq n, m \leq 7\),这个时候 \(Dinic\) 就飞快了(当然也可以打表)

// ubsan: undefined
// accoders
#include <set>
#include <map>
#include <ctime>
#include <cmath>
#include <queue>
#include <stack>
#include <cstdio>
#include <vector>
#include <random>
#include <cstdlib>
#include <cstring>
#include <iostream>
#include <algorithm>
using namespace std;
#define fi first
#define se second
#define db double
#define LL long long
//#define int long long
#define PII pair<int, int>
#define ULL unsigned long long
#define MP(x, y) make_pair(x, y)
#define rep(i, j, k) for (int i = (j); i <= (k); i++)
#define per(i, j, k) for (int i = (j); i >= (k); i--)

template <typename T>
void read(T &x) {
    x = 0;
    T f = 1;
    char ch = getchar();
    while (ch < '0' || ch > '9') {
        if (ch == '-')
            f = -1;
        ch = getchar();
    }
    while (ch >= '0' && ch <= '9') {
        x = (x << 3) + (x << 1) + ch - '0';
        ch = getchar();
    }
    x *= f;
}
template <typename T, typename... Args>
void read(T &x, Args &... Arg) {
    read(x), read(Arg...);
}
const int MaxPrint = 1000;
int Poi_For_Print, Tmp_For_Print[MaxPrint + 5];
template <typename T>
void write(T x) {
    if (x == 0) {
        putchar('0');
        return;
    }
    bool flag = (x < 0 ? 1 : 0);
    x = (x < 0 ? -x : x);
    while (x) Tmp_For_Print[++Poi_For_Print] = x % 10, x /= 10;
    if (flag)
        putchar('-');
    while (Poi_For_Print) putchar(Tmp_For_Print[Poi_For_Print--] + '0');
}
template <typename T, typename... Args>
void write(T x, Args... Arg) {
    write(x);
    putchar(' ');
    write(Arg...);
}
template <typename T, typename... Args>
void print(T x, char ch) {
    write(x);
    putchar(ch);
}
template <typename T>
T Max(T x, T y) {
    return x > y ? x : y;
}
template <typename T>
T Min(T x, T y) {
    return x < y ? x : y;
}
template <typename T>
T Abs(T x) {
    return x > 0 ? x : -x;
}

const int Inf = 0x3f3f3f3f;

int T, n, m, sn, sm;

const int Maxflown = 7 * 1e3 + 2;
const int Maxflowm = 1e5;
struct edge {
    int to[Maxflowm * 2 + 5], Next[Maxflowm * 2 + 5];
    LL flux[Maxflowm * 2 + 5];
    int len, Head[Maxflown + 5];
    edge() {
        len = 1;
        memset(Head, 0, sizeof Head);
    }
    void Init() {
        len = 1;
        memset(Head, 0, sizeof Head);
    }
    void plus(int x, int y, LL w) {
        to[++len] = y;
        Next[len] = Head[x];
        flux[len] = w;
        Head[x] = len;
    }
    void add(int x, int y, LL w) {
        plus(x, y, w);
        plus(y, x, 0);
    }
    void rev_add(int x, int y, LL w) {
        plus(y, x, w);
        plus(x, y, 0);
    }
};
struct Max_Flow {
    int S, T;
    edge mp;

    int hh, tt, q[Maxflown + 5];
    int depth[Maxflown + 5], cur[Maxflown + 5];
    void add(int x, int y, int _flux) { mp.plus(x, y, _flux); }
    void Init() {
        S = Maxflown - 1, T = Maxflown;
        mp.Init();
    }
    bool BFS() {
        rep(i, 1, tt) depth[q[i]] = 0;
        hh = 1;
        tt = 0;
        q[++tt] = S;
        depth[S] = 1;
        cur[S] = mp.Head[S];
        while (hh <= tt) {
            int u = q[hh++];
            for (int i = mp.Head[u]; i; i = mp.Next[i]) {
                int v = mp.to[i];
                LL w = mp.flux[i];
                if (w == 0)
                    continue;
                if (depth[v])
                    continue;
                depth[v] = depth[u] + 1;
                cur[v] = mp.Head[v];
                q[++tt] = v;
                if (v == T)
                    return 1;
            }
        }
        return 0;
    }
    LL DFS(int u, LL Up) {
        if (u == T)
            return Up;
        if (Up == 0)
            return 0;
        LL flow = 0;
        while (cur[u] && flow < Up) {
            int i = cur[u];
            cur[u] = mp.Next[cur[u]];
            int v = mp.to[i];
            LL w = mp.flux[i];
            if (w == 0)
                continue;
            if (depth[v] != depth[u] + 1)
                continue;
            LL tmp = DFS(v, Min(Up - flow, w));
            if (tmp == 0)
                depth[v] = -1;
            flow += tmp;
            mp.flux[i] -= tmp;
            mp.flux[i ^ 1] += tmp;
            if (mp.flux[i] && flow >= Up)
                cur[u] = i;
        }
        return flow;
    }
    LL Dinic() {
        LL flow = 0;
        while (BFS()) {
            flow += DFS(S, Inf);
        }
        return flow;
    }
} G;

int tox[10] = { 0, -2, -2, -1, -1, 1, 1, 2, 2 };
int toy[10] = { 0, 1, -1, 2, -2, 2, -2, 1, -1 };
int Hash(int x, int y) { return (x - 1) * sm + y; }
int Hash_Now(int x, int y) { return (x - 1) * m + y; }
int Getx(int x) { return (x - 1) / sm + 1; }
int GetNowx(int x) { return (x - 1) / m + 1; }
int Gety(int x) { return (x - 1) % sm + 1; }
int GetNowy(int x) { return (x - 1) % m + 1; }

signed main() {
    // freopen ("1.in", "r", stdin);
    // freopen ("1.out", "w", stdout);
    freopen("knight.in", "r", stdin);
    freopen("knight.out", "w", stdout);

    read(T);
    // printf ("%d\n", T);
    while (T--) {
        G.Init();
        read(n, m), sn = n, sm = m;
        // printf ("%d %d\n", n, m);
        vector<PII> ans;
        while (n * m >= Maxflown - 2) {
            if (n & 1) {
                ans.push_back(MP(Hash(1, m - 3), Hash(3, m - 2)));
                ans.push_back(MP(Hash(1, m - 2), Hash(2, m)));
                ans.push_back(MP(Hash(1, m - 1), Hash(3, m)));
                ans.push_back(MP(Hash(1, m), Hash(2, m - 2)));
                ans.push_back(MP(Hash(2, m - 3), Hash(3, m - 1)));
                ans.push_back(MP(Hash(2, m - 1), Hash(3, m - 3)));
                for (int i = 4; i <= n; i += 2) {
                    ans.push_back(MP(Hash(i, m - 3), Hash(i + 1, m - 1)));
                    ans.push_back(MP(Hash(i + 1, m - 3), Hash(i, m - 1)));
                    ans.push_back(MP(Hash(i, m - 2), Hash(i + 1, m)));
                    ans.push_back(MP(Hash(i + 1, m - 2), Hash(i, m)));
                }
            } else {
                for (int i = 1; i <= n; i += 2) {
                    ans.push_back(MP(Hash(i, m - 3), Hash(i + 1, m - 1)));
                    ans.push_back(MP(Hash(i + 1, m - 3), Hash(i, m - 1)));
                    ans.push_back(MP(Hash(i, m - 2), Hash(i + 1, m)));
                    ans.push_back(MP(Hash(i + 1, m - 2), Hash(i, m)));
                }
            }
            m -= 4;
        }
        rep(i, 1, n) {
            rep(j, 1, m) {
                if ((i + j) & 1) {
                    G.add(Hash_Now(i, j), G.T, 1);
                    G.add(G.T, Hash_Now(i, j), 0);
                    continue;
                }
                G.add(G.S, Hash_Now(i, j), 1);
                G.add(Hash_Now(i, j), G.S, 0);
                rep(op, 1, 8) {
                    int x = i + tox[op], y = j + toy[op];
                    if (x < 1 || x > n)
                        continue;
                    if (y < 1 || y > m)
                        continue;
                    // printf ("(%d, %d) -> (%d, %d)\n", i, j, x, y);
                    G.add(Hash_Now(i, j), Hash_Now(x, y), 1);
                    G.add(Hash_Now(x, y), Hash_Now(i, j), 0);
                }
            }
        }
        G.Dinic();
        for (int i = 2; i <= G.mp.len; i += 2) {
            int x = G.mp.to[i ^ 1], y = G.mp.to[i], _flux = G.mp.flux[i];
            // printf ("x = %d, y = %d\n", x, y);
            if (!_flux && 1 <= x && x <= n * m && 1 <= y && y <= n * m) {
                ans.push_back(MP(Hash(GetNowx(x), GetNowy(x)), Hash(GetNowx(y), GetNowy(y))));
            }
        }
        print(ans.size(), '\n');
        for (auto it : ans) {
            print(Getx(it.fi), ' '), print(Gety(it.fi), ' '), print(Getx(it.se), ' '),
                print(Gety(it.se), '\n');
        }
    }
    return 0;
}

T3.嘉心糖

题意:\(x, y\) 贴贴就连一条无向边,求图的最大团。

随机化可过,略了(\(DAY^{-1}\))。

正解:

春春的结论题(😀)

结论一:最大团点的数量=补图中最大独立集点的数量

那么转化为求补图的最大独立集。

结论二:最大独立集点的数量=最小可重路径覆盖(闭包情况下)

考虑人 \(x, y, z\) 之间的贴贴关系,容易知道 \(x, y\) 没有贴贴且 \(y, z\) 没有贴贴,那么 \(x, z\) 肯定没有贴贴,因为贴贴过程中一定 \(x < y < z\),体现在补图上就是给边定向(小指向大,大指向小都一样),这个图就是一个闭包。

结论三:最小不可重路径覆盖=总点数-拆点后二分图最大匹配数

照做即可。

感性证明:

结论一,最大独立集两两之间不相连,在原图上即为两两相连,那么就是一个完全子图,由于是最大独立集,那么就是团。

结论二,不知

结论三,

小结

T1 T2 几乎拿到就想到正解了,但是打+调试花了 \(2h\) 多,实际上代码也不是很复杂 🤮,代码实现加油中。

posted @ 2023-03-10 15:31  C2022lihan  阅读(26)  评论(0编辑  收藏  举报