Educational Codeforces Round 113 (Rated for Div. 2) 简单记录

还好第二天有早八,没半夜打,打了就死定了。

开局一张嘴,结论全靠猜。

A

暴力。

B

首先发现,目标为\(1\)的人不可能输,让这些人赢只会让构造的难度变大,所以对局的两方中含有\(1\)的局全部平局。然后考虑双方都是\(2\)的情况,发现当人数很少的时候没法构造,具体来讲是只有\(1\)个或\(2\)个,输出\(-1\);否则将这些目标为\(2\)的人按顺序连成一个环。


    scanf("%d", &T);
    for (; T--; ) {
        scanf("%d", &n);
        scanf("%s", s + 1);
        vector <int> vec;
        for (int i = 1; i <= n; i++) {
            if (s[i] == '2') vec.emplace_back(i);
        }
        if (vec.size() >= 1 && vec.size() <= 2) puts("NO");
        else {
            puts("YES");
            for (int i = 1; i <= n; i++)
                for (int j = 1; j <= n; j++)
                    ans[i][j] = 0;
            if (!vec.empty()) {
                for (int i = 0; i < vec.size() - 1; i++) {
                    int u = vec[i], v = vec[i + 1];
                    ans[u][v] = '+', ans[v][u] = '-';
                }
                int u = vec[vec.size() - 1], v = vec[0];
                ans[u][v] = '+', ans[v][u] = '-'; 
            }
            for (int i = 1; i <= n; i++) {
                for (int j = 1; j <= n; j++) {
                    if (i == j) ans[i][j] = 'X';
                    else if (s[i] == '1' || s[j] == '1') ans[i][j] = '=';
                    else if (!ans[i][j]) ans[i][j] = '=';
                }
            }
            for (int i = 1; i <= n; i++) {
                for (int j = 1; j <= n; j++) putchar(ans[i][j]);
                puts("");
            }
        }
    }

C

  • 题意写得不怎么清楚 ×
  • 我的英文水平太差了 ✓

题意是这样的,给一个长度为\(n\)的数组\(a[1 : n]\),一个合法的\(1 - n\)的排列定义为:将\(1 - n\)按照排列的顺序按顺序写下\(1 - n\)\(i\)出现\(a_i\)次,如果某个数的出现次数不够则跳过,若写下的这串数没有一个数连续出现两次则合法。求合法的排列的总数。

不得不说真的不擅长做这样的题。

手玩一下小数据,发现当最大的数只有一个的时候不能出现在排列的最后,也就是说,至少有一个跟它相同或者次大的数出现在它的后面,写得严格一点是这样的:

\(a\)中的数构成的\(\text{set}\),记为\(s\)

  • \(size(s) = 1\),合法,答案为\(n!\)

  • \(size(s) \geq 2\),此时记最大值为\(mx\),次大值为\(mx2\):

    • \(mx\)有超过一个,此时也一定合法,答案为\(n!\)

    • \(mx\)只有一个,此时至少有一个\(mx2\)出现在\(mx\)的后面,考虑所有不合法的情况,答案为\(n! - \binom{n}{1 + cnt(mx2)}((cnt(mx2))!)((n - 1 - cnt(mx2))!)\)

半夜基本上猜错就没了,心态会炸。

#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
typedef double db;
typedef pair <int, int> pin;

#ifndef ONLINE_JUDGE
bool MEMORY_ST;
#endif

const int N = 2e5 + 5;
const int Maxn = 2e5;
const ll P = 998244353LL;

int T, n, a[N];
ll fac[N], ifac[N], inv[N];

namespace Fread {
    const int L = 1 << 15;
    
    char buffer[L], *S, *T;
    
    inline char Getchar() {
        if(S == T) {
            T = (S = buffer) + fread(buffer, 1, L, stdin);
            if(S == T) return EOF;
        }
        return *S++;
    }
    
    template <class T> 
    inline void read(T &X) {
        char ch; T op = 1;
        for(ch = Getchar(); ch > '9' || ch < '0'; ch = Getchar())
            if(ch == '-') op = -1;
        for(X = 0; ch >= '0' && ch <= '9'; ch = Getchar()) 
            X = (X << 1) + (X << 3) + ch - '0'; 
        X *= op;
    }
    
} using Fread::read;   

inline ll fpow(ll x, ll y) {
    ll res = 1;
    for (x %= P; y > 0; y >>= 1) {
        if (y & 1) res = res * x % P;
        x = x * x % P;
    }
    return res;
}

inline ll binom(int x, int y) {
    if (x < 0 || y < 0 || y > x) return 0;
    return fac[x] * ifac[y] % P * ifac[x - y] % P;
}

#ifndef ONLINE_JUDGE
bool MEMORY_ED;
#endif

int main() {
#ifndef ONLINE_JUDGE
    freopen("sample.in", "r", stdin);
    clock_t st_clock = clock();
#endif

    fac[0] = 1;
    for (int i = 1; i <= Maxn; i++) fac[i] = fac[i - 1] * i % P;
    ifac[Maxn] = fpow(fac[Maxn], P - 2);
    for (int i = Maxn - 1; i >= 0; i--) ifac[i] = ifac[i + 1] * (i + 1) % P;
    for (int i = 1; i <= Maxn; i++) inv[i] = fpow(i, P - 2);

    read(T);
    for (; T--; ) {
        read(n);
        vector <int> vec;
        for (int i = 1; i <= n; i++) read(a[i]);
        for (int i = 1; i <= n; i++) vec.emplace_back(a[i]);
        sort(vec.begin(), vec.end());
        vector <int>::iterator end = unique(vec.begin(), vec.end());
        vec.erase(end, vec.end());
        if (vec.size() == 1) printf("%lld\n", fac[n]);
        else {
            int mx = vec[vec.size() - 1], mx2 = vec[vec.size() - 2];
            map <int, int> cnt;
            for (int i = 1; i <= n; i++) ++cnt[a[i]];
            ll ans = fac[n];
            if (cnt[mx] == 1) {
                if (mx - mx2 >= 2) ans = 0;
                else {
                    int c = 1 + cnt[mx2];
                    ans -= 1LL * binom(n, c) * fac[n - c] % P * fac[c - 1] % P;
                    if (ans < 0) ans += P;                    
                }
            }
            printf("%lld\n", ans);
        }
    }

#ifndef ONLINE_JUDGE
    clock_t ed_clock = clock();
    printf("time = %f ms\n", (double)(ed_clock - st_clock) / CLOCKS_PER_SEC * 1000);
    printf("memory = %.2f MB\n", (&MEMORY_ED - &MEMORY_ST) / 1024.0 / 1024.0);
#endif
    return 0;
}

D

题意:在\([0, 10^6]^2\)的网格中画了\(n\)条横线和\(m\)条竖线,给出\(k\)个点,点的坐标至少在一条线上。定义两个点之间的距离为只能走画出来的线从一个点到另一个点的最短距离;若一个无序点对\((p_1, p_2)\)中两点的距离严格大于曼哈顿距离,则称这两个点“不便捷”,求所有的“不便捷”的点对的数量。

将在横线上的点称为“横点”,将在竖线上的点称为“竖点”,注意到有一类点可以既是横点又是竖点,很特殊。

画画图可以发现,横点和竖点之间一定是“便捷”的,也就是说不便捷的点对只可能出现在横点和竖点的集合内部,而那类特殊的点不会有贡献。

单独考虑横点,将所有的横点和竖线的横坐标排排序数数点就可以统计贡献了;特别注意考虑两个纵坐标相同的横点是没有贡献的,样例有这种情况,不算太坑。

一开始写了个unordered_map被卡成了猪头,然后这个题的读入量和处理的量相当大,基本上要尽可能全部写成\(O(n)\),除了一个\(200000\)长度的排序。

#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
typedef double db;
typedef pair <int, int> pin;

#ifndef ONLINE_JUDGE
bool MEMORY_ST;
#endif

const int N = 3e5 + 5;
const int M = 1e6 + 5;
const ll P = 998244353LL;

int T, n, m, qn, xi[N], yi[N], px[N], py[N];
bool isx[N], isy[N], ex[M], ey[M];

namespace Fread {
    const int L = 1 << 15;
    
    char buffer[L], *S, *T;
    
    inline char Getchar() {
        if(S == T) {
            T = (S = buffer) + fread(buffer, 1, L, stdin);
            if(S == T) return EOF;
        }
        return *S++;
    }
    
    template <class T> 
    inline void read(T &X) {
        char ch; T op = 1;
        for(ch = Getchar(); ch > '9' || ch < '0'; ch = Getchar())
            if(ch == '-') op = -1;
        for(X = 0; ch >= '0' && ch <= '9'; ch = Getchar()) 
            X = (X << 1) + (X << 3) + ch - '0'; 
        X *= op;
    }
    
} using Fread::read;   

bool cmp1(pin u, pin v) {
    return u.first < v.first;
}

bool cmp2(pin u, pin v) {
    return u.second < v.second;
}

namespace Counter {
    int cnt[M], sum;
    vector <int> val;

    inline void clear() {
        sum = 0;
        for (int i = 0; i < val.size(); i++) {
            int v = val[i];
            cnt[v] = 0;
        }
        val.clear();
    }

} using namespace Counter;

#ifndef ONLINE_JUDGE
bool MEMORY_ED;
#endif

int main() {
#ifndef ONLINE_JUDGE
    freopen("sample.in", "r", stdin);
    clock_t st_clock = clock();
#endif

    read(T);
    for (; T--; ) {
        read(n), read(m), read(qn);
        for (int i = 1; i <= n; i++) {
            read(xi[i]);
            ex[xi[i]] = 1;
        } 
        for (int i = 1; i <= m; i++) {
            read(yi[i]);
            ey[yi[i]] = 1;
        } 
        for (int i = 1; i <= qn; i++) {
            read(px[i]), read(py[i]);
            isx[i] = isy[i] = 0;
            if (ex[px[i]]) isx[i] = 1;
            if (ey[py[i]]) isy[i] = 1;
        } 
        ll ans = 0;
        vector <pin> pot, t1, t2;
        for (int i = 1; i <= m; i++) t1.emplace_back(pin(-1, yi[i]));
        for (int i = 1; i <= qn; i++) {
            if (isx[i] && isy[i]) continue;
            if (isx[i]) t2.emplace_back(pin(px[i], py[i]));
        }
        sort(t2.begin(), t2.end(), cmp2);
        for (int p = 0, q = 0; p < t1.size() || q < t2.size(); ) {
            if (p == t1.size()) {
                pot.emplace_back(t2[q++]);
            } else if (q == t2.size()) {
                pot.emplace_back(t1[p++]);
            } else {
                if (t1[p].second < t2[q].second) pot.emplace_back(t1[p++]);
                else pot.emplace_back(t2[q++]);
            }
        }
        clear();
        for (int i = 0; i < pot.size(); i++) {
            pin cur = pot[i];
            int x = cur.first, y = cur.second;
            if (x == -1) {
                clear();
                sum = 0;
            } else {
                ans += sum - cnt[x];
                if (cnt[x] == 0) val.emplace_back(x);
                ++cnt[x];
                ++sum;
            }
        }
        // printf("%lld\n", ans);
        pot.clear(), t1.clear(), t2.clear();
        for (int i = 1; i <= n; i++) t1.emplace_back(pin(xi[i], -1));
        for (int i = 1; i <= qn; i++) {
            if (isx[i] && isy[i]) continue;
            if (isy[i]) t2.emplace_back(pin(px[i], py[i]));
        }
        sort(t2.begin(), t2.end(), cmp1);
        for (int p = 0, q = 0; p < t1.size() || q < t2.size(); ) {
            if (p == t1.size()) {
                pot.emplace_back(t2[q++]);
            } else if (q == t2.size()) {
                pot.emplace_back(t1[p++]);
            } else {
                if (t1[p].first < t2[q].first) pot.emplace_back(t1[p++]);
                else pot.emplace_back(t2[q++]);
            }
        }
        clear();
        sum = 0;
        for (int i = 0; i < pot.size(); i++) {
            pin cur = pot[i];
            int x = cur.first, y = cur.second;
            if (y == -1) {
                clear();
                sum = 0;
            } else {
                ans += sum - cnt[y];
                if (cnt[y] == 0) val.emplace_back(y);
                ++cnt[y];
                ++sum;
            }
        }
        printf("%lld\n", ans);
        for (int i = 1; i <= n; i++) ex[xi[i]] = 0;
        for (int i = 1; i <= m; i++) ey[yi[i]] = 0;
    }

#ifndef ONLINE_JUDGE
    clock_t ed_clock = clock();
    printf("time = %f ms\n", (double)(ed_clock - st_clock) / CLOCKS_PER_SEC * 1000);
    printf("memory = %.2f MB\n", (&MEMORY_ED - &MEMORY_ST) / 1024.0 / 1024.0);
#endif
    return 0;
}

E

题意应该很好懂。

考虑不同的情况一共只有\(2^{2^k - 1}\)种,当\(k \leq 4\)时可以直接暴力做,接下来只考虑\(k = 5\)的情况。

这种哈希值的逆向计算基本上是不太可能的,考虑优化枚举方式,尝试折半。先考虑\([1, 16]\)区间中的情况,不考虑最后一场决赛的胜者,一共有\(2^{15}\)种不同的情况,这些情况与\([17, 32]\)中的情况相互独立,这样的话我们分别枚举冠军和亚军落在\([1, 16]\)\([17, 32]\)中的不同情况,用\(\text{map}\)组合一下就可以找到解了。

具体方法是:先计算\([1, 16]\)中的所有情况\((s, h_1)\)存进\(\text{map}\),然后计算\([17, 32]\)中的所有情况,假设当前胜负情况为\((t, h_2)\),如果我们能在\(\text{map}\)中找到一个\(h_1 + h_2 \equiv h\mod P\)就算成功,考虑\(h_1\)\(h_2\)的取值范围,只有当\(h_1 = h - h_2\)\(h - h_2 + P\)才有可能。

代码写得巨啰嗦。

#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
typedef double db;
typedef pair <int, int> pin;

#ifndef ONLINE_JUDGE
bool MEMORY_ST;
#endif

const int N = 10005;
const int Maxn = 17;
const ll P = 998244353LL;

int n, k;
ll b, h, bin[N];

namespace Fread {
    const int L = 1 << 15;
    
    char buffer[L], *S, *T;
    
    inline char Getchar() {
        if(S == T) {
            T = (S = buffer) + fread(buffer, 1, L, stdin);
            if(S == T) return EOF;
        }
        return *S++;
    }
    
    template <class T> 
    inline void read(T &X) {
        char ch; T op = 1;
        for(ch = Getchar(); ch > '9' || ch < '0'; ch = Getchar())
            if(ch == '-') op = -1;
        for(X = 0; ch >= '0' && ch <= '9'; ch = Getchar()) 
            X = (X << 1) + (X << 3) + ch - '0'; 
        X *= op;
    }
    
} using Fread::read;   

inline void inc(ll &x, ll y) {
    x += y;
    if (x >= P) x -= P;
}

namespace Sub1 {
    int tot, cnt, a[N][2], p[N], rk[N];

    inline bool chk(int s) {
        for (int i = 1; i <= n; i++) p[i] = 0;
        cnt = 0;
        for (int i = 1; i <= n / 2; i++) {
            ++cnt;
            a[cnt][0] = 2 * i - 1;
            a[cnt][1] = 2 * i;
        }
        int cur = n;
        bool one = 0;
        for (int i = 0; i < tot; i++) {
            if (s & (1 << i)) {
                /* first player wins */
                p[a[i + 1][1]] = rk[cur--];
                if (!one) {
                    ++cnt;
                    a[cnt][0] = a[i + 1][0];
                    one = 1;
                } else {
                    a[cnt][1] = a[i + 1][0];
                    one = 0;
                }
            } else {
                /* second player wins */
                p[a[i + 1][0]] = rk[cur--];
                if (!one) {
                    ++cnt;
                    a[cnt][0] = a[i + 1][1];
                    one = 1;
                } else {
                    a[cnt][1] = a[i + 1][1];
                    one = 0;
                }
            }
        }
        for (int i = 1; i <= n; i++)
            if (!p[i]) 
                p[i] = 1;
        ll res = 0;
        for (int i = 1; i <= n; i++) {
            inc(res, 1LL * i * bin[p[i]] % P);
        }
        return (res == h);
    }

    inline void solve() {
        rk[1] = 1;
        for (int cur = 1, i = 0; i < k; i++) {
            int tmp = cur + 1;
            for (int j = 0; j < (1 << i); j++) rk[++cur] = tmp;
        }
        tot = n - 1;
        bool flag = 0;
        for (int s = 0; s < (1 << tot); s++) {
            if (chk(s)) {
                flag = 1;
                for (int i = 1; i <= n; i++)
                    printf("%d%c", p[i], " \n"[i == n]);
                break;
            }
        }
        if (!flag) puts("-1");
    }

}

namespace Sub2 {
    int tot, cnt, a[N][2], p[N], rk[N];
    map <ll, int> mp;

    inline ll calc(int s, bool half, bool fir) {
        rk[1] = fir ? 1 : 2;
        cnt = 0;
        if (half) {
            /* [17, 32] */
            for (int i = 17; i <= 32; i++) p[i] = 0;
            for (int i = 1; i <= 8; i++) {
                ++cnt;
                a[cnt][0] = 2 * i - 1 + 16;
                a[cnt][1] = 2 * i + 16;
            }
        } else {
            /* [1, 16] */
            for (int i = 1; i <= 16; i++) p[i] = 0;
            for (int i = 1; i <= 8; i++) {
                ++cnt;
                a[cnt][0] = 2 * i - 1;
                a[cnt][1] = 2 * i;
            }
        }
        int cur = 16;
        bool one = 0;
        for (int i = 0; i < 15; i++) {
            if (s & (1 << i)) {
                /* first player wins */
                p[a[i + 1][1]] = rk[cur--];
                if (!one) {
                    ++cnt;
                    a[cnt][0] = a[i + 1][0];
                } else {
                    a[cnt][1] = a[i + 1][0];
                }
                one ^= 1;
            } else {
                /* second player wins */
                p[a[i + 1][0]] = rk[cur--];
                if (!one) {
                    ++cnt;
                    a[cnt][0] = a[i + 1][1];
                } else {
                    a[cnt][1] = a[i + 1][1];
                }
                one ^= 1;
            }
        }
        ll res = 0;
        if (half) {
            for (int i = 17; i <= 32; i++) 
                if (!p[i])
                    p[i] = rk[1];
            for (int i = 17; i <= 32; i++)
                inc(res, 1LL * i * bin[p[i]] % P);
        } else {
            for (int i = 1; i <= 16; i++)
                if (!p[i])
                    p[i] = rk[1];
            for (int i = 1; i <= 16; i++)
                inc(res, 1LL * i * bin[p[i]] % P);
        }
        return res;
    }

    inline int get(ll curh) {
        ll cur = h - curh;
        if (mp.find(cur) != mp.end()) return mp[cur];
        cur += P;
        if (mp.find(cur) != mp.end()) return mp[cur];
        return -1;
    }

    inline void solve() {
        rk[2] = 3, rk[3] = rk[4] = 5, rk[5] = rk[6] = rk[7] = rk[8] = 9;
        for (int i = 9; i <= 16; i++) rk[i] = 17;
        /* the winner of [1, 16] wins the final round */
        mp.clear();
        int curTot = n / 2 - 1;
        bool flag = 0;
        for (int s = 0; s < (1 << curTot); s++) {
            ll curh = calc(s, 0, 1);
            mp[curh] = s;
        }
        for (int s = 0; s < (1 << curTot); s++) {
            ll curh = calc(s, 1, 0);
            int t = get(curh);
            if (t != -1) {
                flag = 1;
                calc(s, 1, 0);
                calc(t, 0, 1);
                for (int i = 1; i <= 32; i++)
                    printf("%d%c", p[i], " \n"[i == 32]);
                break;
            }
        }
        if (flag) return;
        /* the winner of [17, 32] wins the final round */
        mp.clear();
        for (int s = 0; s < (1 << curTot); s++) {
            ll curh = calc(s, 1, 1);
            mp[curh] = s;
        }
        for (int s = 0; s < (1 << curTot); s++) {
            ll curh = calc(s, 0, 0);
            int t = get(curh);
            if (t != -1) {
                flag = 1;
                calc(s, 0, 0);
                calc(t, 1, 1);
                for (int i = 1; i <= 32; i++)
                    printf("%d%c", p[i], " \n"[i == 32]);
                break;
            }
        }
        if (!flag) puts("-1");
    }
}

#ifndef ONLINE_JUDGE
bool MEMORY_ED;
#endif

int main() {
#ifndef ONLINE_JUDGE
    freopen("sample.in", "r", stdin);
    clock_t st_clock = clock();
#endif

    read(k), read(b), read(h);
    n = 1 << k;
    bin[0] = 1;
    for (int i = 1; i <= Maxn; i++) bin[i] = bin[i - 1] * b % P;
    if (k <= 4) Sub1::solve();
    else Sub2::solve();

#ifndef ONLINE_JUDGE
    clock_t ed_clock = clock();
    printf("time = %f ms\n", (double)(ed_clock - st_clock) / CLOCKS_PER_SEC * 1000);
    printf("memory = %.2f MB\n", (&MEMORY_ED - &MEMORY_ST) / 1024.0 / 1024.0);
#endif
    return 0;
}

F

待填坑

总之就是非常暴力,而且很难写,反正我在场上是写不出来。

以下基本来自题解。

答案最大为\(12^{12}\),有一点点大,但是又不是特别大;可以考虑枚举相同的数有哪些,这样的集合数量为第\(12\)个贝尔数\(B_{12} \approx 4\times 10^6\)

考虑怎么检验答案,一个很自然的想法是状压dp,设\(f(s, i, j)\)表示当前走过的集合为\(s\),最后一步停在\(i\)\(j\)可不可行,每次选出两个点加入集合。

每个可行的划分都检验一遍肯定不行,考虑优化检验过程。如果我们将这\(n\)个数组成\(\frac{n}{2}\)\(\text{pair}\),就可以用上面所需时间开根号检验,这样的\(\text{pair}\)划分一共有\(11 \times 9 \times 7 \times 5 \times 3 = 10395\)个,检验一次需要使用\(2^6\times 6 \times 6\)的时间。

然后咋检验呢,对于每个不同的划分,如果我们将其中相同的比较大块的部分接着划分,划分到\(\text{pair}\)的级别,且存在一个合法的\(\text{pair}\),那么这个划分就是合法的。这是一个高维前缀和的形式。

对于所有的划分和\(\text{pair}\),数量不超过\(6\)个,考虑采用\(6\)进制数存状态,按照每个划分出现的第一个位置从小到大编号,这样的状态数量不超过上面提到的约四百万个。

然后对于一个划分,枚举不小于\(4\)的集合,将其第一个数和后面的数暴力地拆开做记忆化搜索即可。

#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
typedef double db;
typedef pair <int, int> pin;

#ifndef ONLINE_JUDGE
bool MEMORY_ST;
#endif

const int N = 12;
const int M = 1 << 12;
const ll P = 998244353LL;

int n, m, k, cnt[M];
ll fac[N << 1], ans = 0, bin[N];
bool e[N][N], f[M][N];
map <ll, bool> ok;
vector <int> g, bel;

namespace Fread {
    const int L = 1 << 15;
    
    char buffer[L], *S, *T;
    
    inline char Getchar() {
        if(S == T) {
            T = (S = buffer) + fread(buffer, 1, L, stdin);
            if(S == T) return EOF;
        }
        return *S++;
    }
    
    template <class T> 
    inline void read(T &X) {
        char ch; T op = 1;
        for(ch = Getchar(); ch > '9' || ch < '0'; ch = Getchar())
            if(ch == '-') op = -1;
        for(X = 0; ch >= '0' && ch <= '9'; ch = Getchar()) 
            X = (X << 1) + (X << 3) + ch - '0'; 
        X *= op;
    }
    
} using Fread::read;   

inline int lowbit(int s) {
    return s & (-s);
}

inline ll getDivHash() {
    vector <int> tmp(n);
    ll res = 0;
    for (int i = 0; i < g.size(); i++) {
        for (int j = 0; j < n; j++) {
            if (!(g[i] & (1 << j))) continue;
            tmp[j] = i;
        }
    }

    // for (int i = 0; i < g.size(); i++)
    //     printf("%d%c", g[i], " \n"[i == g.size() - 1]);
    // for (int i = 0; i < n; i++)
    //     printf("%d%c", tmp[i], " \n"[i == n - 1]);

    for (int i = 0; i < n; i++) res += bin[i] * tmp[i];
    return res;
}

inline ll getPairHash(vector <int> &v) {
    ll res = 0;
    for (int i = 0; i < n; i++) {
        assert(v[i] != -1);
        res += bin[i] * v[i];
    } 
    return res;
}

inline ll maintain(vector <int> cur) {
    int p = 0;
    vector <int> flag(n, -1);
    for (int i = 0; i < cur.size(); i++) {
        if (flag[cur[i]] == -1) flag[cur[i]] = p++;
        cur[i] = flag[cur[i]];
    }
    return getPairHash(cur);
}

bool dp(ll cur) {
    if (ok.find(cur) != ok.end()) return ok[cur];
    ll res = false;
    vector <int> tmp(n);
    ll t = cur;
    for (int i = 0; i < n; i++) {
        tmp[i] = t % 6;
        t /= 6;
    }
    int tot = n / 2;
    vector <int> valCnt(tot);
    for (int i = 0; i < n; i++) ++valCnt[tmp[i]];
    for (int i = 0; i < n; i++) {
        if (valCnt[tmp[i]] <= 2) continue;
        int oldVal = tmp[i];
        for (int j = i + 1; j < n; j++) {
            if (tmp[j] != tmp[i]) continue;
            tmp[i] = tmp[j] = tot;
            if (dp(maintain(tmp))) {
                res = true;
                break;
            }
            tmp[i] = tmp[j] = oldVal;
        }
        break;
    }
    ok[cur] = res;
    return res;
}

inline bool chkDiv() {
    ll cur = getDivHash();
    return dp(cur);
}

inline void chkPair() {
    // for (int i = 0; i < n; i++)
    //     printf("%d%c", bel[i], " \n"[i == n - 1]);

    int tot = n / 2;
    vector <int> p1(tot, -1), p2(tot, -1);
    for (int i = 0; i < n; i++) {
        if (p1[bel[i]] == -1) {
            p1[bel[i]] = i;
        } else {
            assert(p2[bel[i]] == -1);
            p2[bel[i]] = i;
        }
    }
    for (int s = 0; s < (1 << tot); s++) 
        for (int i = 0; i < tot; i++)
            f[s][i] = 0;
    for (int i = 0; i < tot; i++) f[1 << i][i] = 1;
    for (int s = 1; s < (1 << tot); s++) {
        for (int i = 0; i < tot; i++) {
            if (!(s & (1 << i))) continue;
            if (!f[s][i]) continue;
            int x1 = p1[i], y1 = p2[i];
            for (int j = 0; j < tot; j++) {
                if (s & (1 << j)) continue;
                int x2 = p1[j], y2 = p2[j];
                if ((e[x1][x2] && e[y1][y2]) || (e[x1][y2] && e[x2][y1])) {
                    f[s ^ (1 << j)][j] = 1;
                }
            }
        }
    }
    ll cur = getPairHash(bel);
    bool res = false;
    for (int i = 0; i < tot; i++) {
        if (!f[(1 << tot) - 1][i]) continue;
        if (e[p1[i]][p2[i]]) {
            res = true;
            break;
        }
    }
    ok[cur] = res;
    // printf("%lld %d\n", cur, res);
}

void dfsPair(int s, int cur) {
    if (s == (1 << n) - 1) {
        chkPair();
        return;
    }
    int p = 0;
    for (int i = 0; i < n; i++) {
        if (!(s & (1 << i))) {
            p = i;
            break;
        }
    }
    for (int i = p + 1; i < n; i++) {
        if (s & (1 << i)) continue;
        bel[p] = bel[i] = cur;
        int tos = s ^ (1 << p) ^ (1 << i);
        dfsPair(tos, cur + 1);
        bel[p] = bel[i] = -1;
    }
}

void dfsDiv(int s) {
    if (s == (1 << n) - 1) {
        if (chkDiv()) {
            // for (int i = 0; i < g.size(); i++)
            //     printf("%d%c", g[i], " \n"[i == g.size() - 1]);
            if (g.size() <= k) {
                ll res = fac[k] / fac[k - g.size()];
                ans += res;                
            }
        }
        return;
    }
    int r = ((1 << n) - 1) ^ s, x = lowbit(r);
    for (int sub = r; sub; sub = (sub - 1) & r) {
        if (!(sub & x)) continue;
        if (cnt[sub] & 1) continue;
        g.emplace_back(sub);
        dfsDiv(s ^ sub);
        g.pop_back();
    }
}

#ifndef ONLINE_JUDGE
bool MEMORY_ED;
#endif

int main() {
#ifndef ONLINE_JUDGE
    freopen("sample.in", "r", stdin);
    clock_t st_clock = clock();
#endif

    /*     */

    read(n), read(m), read(k);
    fac[0] = 1;
    for (int i = 1; i <= k; i++) fac[i] = fac[i - 1] * i;
    for (int s = 0; s < (1 << n); s++) cnt[s] = __builtin_popcount(s);
    bin[0] = 1;
    for (int i = 1; i < n; i++) bin[i] = bin[i - 1] * 6;
    for (int x, y, i = 1; i <= m; i++) {
        read(x), read(y);
        x--, y--;
        e[x][y] = e[y][x] = 1;
    }
    bel.resize(n, -1);
    dfsPair(0, 0);
    dfsDiv(0);
    printf("%lld\n", ans);

#ifndef ONLINE_JUDGE
    clock_t ed_clock = clock();
    printf("time = %f ms\n", (double)(ed_clock - st_clock) / CLOCKS_PER_SEC * 1000);
    printf("memory = %.2f MB\n", (&MEMORY_ED - &MEMORY_ST) / 1024.0 / 1024.0);
#endif
    return 0;
}

cf评测机的diagnose也太神了。

posted @ 2021-09-11 09:46  CzxingcHen  阅读(33)  评论(0编辑  收藏  举报