.)所有题目思路+代码实现

感觉写的很详细了呢,喵。

update 1.24, [03:40] 赛时通过的题目。

update 1.24, [04:39] 赛时挂的M。

update 1.24, [07:45] I。

update 1.25, [13:02] L。

A题:一起奏响历史之音!

思路:

检查数组中是否存在 4 或者 7 即可。

代码:

#include <bits/stdc++.h>
using i64 = long long;

int main() {
    std::ios::sync_with_stdio(false);
    std::cin.tie(nullptr);
    
    bool flag = true;
    for (int i = 0; i < 7; i ++ ) {
        int a;
        std::cin >> a;

        if (a == 4 || a == 7) flag = false;
    }

    if (flag) {
        std::cout << "YES\n";
    }else {
        std::cout << "NO\n";
    }
    
    return 0;
}

B题:能去你家蹭口饭吃吗

思路:

后输出中位数

代码:

#include <bits/stdc++.h>
using i64 = long long;

int main() {
    std::ios::sync_with_stdio(false);
    std::cin.tie(nullptr);
    
    int n;
    std::cin >> n;

    std::vector<int> a(n);
    for (int i = 0; i < n; i ++ ) {
        std::cin >> a[i];
    }
    
    std::sort(a.begin(), a.end());
    
    std::cout << a[n / 2] - 1 << '\n';

    return 0;
}

C题:字符串外串

思路:

注:每个人的构造方式可能不同。

我的做法偏贪心。

可以先去看 D 题,对构造此题有帮助。

D 题结论,给定一个字符串,对于每一个小写字符开一个数组存其位置(从小到大存)。字符串的可爱值 k,是所有数组中倒数第二个元素、第二个元素取最大值(正 2 右,倒 2 左)。

的时候,是无解的。

这个可以尝试整个字符串都为同一个字符。

其余情况

我们先去放 ,由上述结论可知,我们肯定要在 + 的位置先放一个。

然后进行分类讨论。

如果 + ,我们则还需在其左右两边放 才能使这个位置上的 成为正 2 或者 倒 2,贪心地放,左右各放一个,我们就放在开头和结尾。

举例:

4 3

???? <- ?aa? <- aaaa

对于出现下列情况。

a???a?????a???a

首先中间的两个 之间都可以填 ,并不会改变 的正二或者倒二。

变成。

a???aaaaaaa???a

假设我们现在要放 ,若我们放好一个在左边,我们不会再放一个在左边,不然就会大于

贪心地放,左右各一个,但不能超过 个。对于一开始的两个 放开头和结尾没有任何问题。

如果 + ,我们不能再在这两个的左右多放 ,这会导致可爱值大于

现在是。

??a????a??

然后我们放 ,如果放在第一个 的左边,我们还可以贪心地在第二个 的右边放一个。

对称消完以后.

bca????acb

可以证明的是,中间不能放两个相同的字符,我们正常填入其他字符即可。

中间过程中如果放入的字符大于 ,那么就是不可以的,这也很好理解,如果给定的字符够多,那我们其实是一定可以构造的。

代码:

#include <bits/stdc++.h>
using i64 = long long;

void Solve() {
    int n, m;
    std::cin >> n >> m;

    if (m == n) {
        std::cout << "NO\n";
        return;
    }

    std::vector<int> ans(n + 1, -1);
    ans[m] = ans[n - m + 1] = 0;

    int id = 0;
    if (m >= n - m + 1) {
        ans[1] = ans[n] = 0;
        
        for (int i = n - m + 1; i <= m; i ++ ) {
            ans[i] = 0;
        }
        
        for (int i = 2, j = n - 1; i < n - m + 1; i ++, j -- ) {
            id ++;
            if (id >= 26) {
                std::cout << "NO\n";
                return;
            }

            ans[i] = ans[j] = id;
        }
    }else {
        for (int i = 1, j = n; i < m; i ++, j -- ) {
            id ++;
            if (id >= 26) {
                std::cout << "NO\n";
                return;
            }

            ans[i] = ans[j] = id;
        }

        for (int i = m + 1; i < n - m + 1; i ++ ) {
            id ++;
            if (id >= 26) {
                std::cout << "NO\n";
                return;
            }

            ans[i] = id;
        }
    }

    std::cout << "YES\n";
    for (int i = 1; i <= n; i ++ ) {
        std::cout << char('a' + ans[i]);
    }
    std::cout << '\n';

}

int main() {
    std::ios::sync_with_stdio(false);
    std::cin.tie(nullptr);
    int tt; std::cin >> tt;
    while (tt --) Solve();
    return 0;
}

D题:字符串里串

思路:

我们考虑最后可爱值为 k 的字符串 s 的开头和结尾。

首先,如果这个字符出现大于 1 次,那么其就可以成为开头或者结尾。

比如,假定 p 和 q 是我们选择的。

???a???a???

pppp
qqq....q

上面是作为结尾的情况,我们考虑开头

.......pppp
...q....qqq

如果给定的是。

???a?a?a???

任意选两个作为开头或结尾,可以证明的是。

???a?a?a???

.....pppppp
...q..qqqqq
或者
qqqqqq.....
ppppp..p...
是最优的。

那么选定的长度是正 2 右和倒 2 左,注意不能选空串,因此答案为 1 的时候将其归零。

代码:

#include <bits/stdc++.h>
using i64 = long long;

int main() {
    std::ios::sync_with_stdio(false);
    std::cin.tie(nullptr);
    
    int n;
    std::cin >> n;

    std::string s;
    std::cin >> s;
    s = ' ' + s;

    std::vector<std::vector<int>> p(26);
    for (int i = 1; i <= n; i ++ ) {
        p[s[i] - 'a'].push_back(i);
    }

    int ans = 0;
    for (int i = 0; i < 26; i ++ ) {
        int m = p[i].size();
        if (m > 1) {
            ans = std::max({ans, p[i][m - 2], n - p[i][1] + 1});
        }
    }
    
    if (ans == 1) ans = 0;
    std::cout << ans << '\n';

    return 0;
}

E题:一起走很长的路!

思路:

考虑一个牌需要多少才会被推倒?

-

那么对于一段区间,则是这些值取大。

我们用线段树存入 - 即可。

注意 l 不需要 “被” 推倒。

代码

#include <bits/stdc++.h>
using i64 = long long;

template<class Info>
struct SegmentTree {
    int n;
    std::vector<Info> info;
    SegmentTree() : n(0) {}
    SegmentTree(int n_, Info v_ = Info()) {
        init(n_, v_);
    }
    template<class T>
    SegmentTree(std::vector<T> init_) {
        init(init_);
    }
    void init(int n_, Info v_ = Info()) {
        init(std::vector(n_, v_));
    }
    template<class T>
    void init(std::vector<T> init_) {
        n = init_.size();
        info.assign(4 << (int)std::log2(n), Info());
        std::function<void(int, int, int)> build = [&](int p, int l, int r) {
            if (r - l == 1) {
                info[p] = init_[l];
                return;
            }
            int m = (l + r) / 2;
            build(2 * p, l, m);
            build(2 * p + 1, m, r);
            pull(p);
        };
        build(1, 0, n);
    }
    void pull(int p) {
        info[p] = info[2 * p] + info[2 * p + 1];
    }
    void modify(int p, int l, int r, int x, const Info &v) {
        if (r - l == 1) {
            info[p] = v;
            return;
        }
        int m = (l + r) / 2;
        if (x < m) {
            modify(2 * p, l, m, x, v);
        } else {
            modify(2 * p + 1, m, r, x, v);
        }
        pull(p);
    }
    void modify(int p, const Info &v) {
        modify(1, 0, n, p, v);
    }
    Info rangeQuery(int p, int l, int r, int x, int y) {
        if (l >= y || r <= x) {
            return Info();
        }
        if (l >= x && r <= y) {
            return info[p];
        }
        int m = (l + r) / 2;
        return rangeQuery(2 * p, l, m, x, y) + rangeQuery(2 * p + 1, m, r, x, y);
    }
    Info rangeQuery(int l, int r) {
        return rangeQuery(1, 0, n, l, r);
    }
    template<class F>
    int findFirst(int p, int l, int r, int x, int y, F &&pred) {
        if (l >= y || r <= x) {
            return -1;
        }
        if (l >= x && r <= y && !pred(info[p])) {
            return -1;
        }
        if (r - l == 1) {
            return l;
        }
        int m = (l + r) / 2;
        int res = findFirst(2 * p, l, m, x, y, pred);
        if (res == -1) {
            res = findFirst(2 * p + 1, m, r, x, y, pred);
        }
        return res;
    }
    template<class F>
    int findFirst(int l, int r, F &&pred) {
        return findFirst(1, 0, n, l, r, pred);
    }
    template<class F>
    int findLast(int p, int l, int r, int x, int y, F &&pred) {
        if (l >= y || r <= x) {
            return -1;
        }
        if (l >= x && r <= y && !pred(info[p])) {
            return -1;
        }
        if (r - l == 1) {
            return l;
        }
        int m = (l + r) / 2;
        int res = findLast(2 * p + 1, m, r, x, y, pred);
        if (res == -1) {
            res = findLast(2 * p, l, m, x, y, pred);
        }
        return res;
    }
    template<class F>
    int findLast(int l, int r, F &&pred) {
        return findLast(1, 0, n, l, r, pred);
    }
};

struct Info {
    i64 max = -1E18;
    Info() {};
    Info(i64 x) : max(x) {};
};

Info operator+(const Info &a, const Info &b) {
    Info c;
    c.max = std::max(a.max, b.max);
    return c;
}

int main() {
    std::ios::sync_with_stdio(false);
    std::cin.tie(nullptr);
    
    int n, q;
    std::cin >> n >> q;

    std::vector<int> a(n);
    for (int i = 0; i < n; i ++ ) {
        std::cin >> a[i];
    }

    std::vector<i64> pre(n + 1);
    for (int i = 0; i < n; i ++ ) {
        pre[i + 1] = pre[i] + a[i];
    }

    std::vector<i64> bk = pre;
    for (int i = 1; i <= n; i ++ ) {
        bk[i] = -pre[i - 1] + a[i - 1];
    }

    SegmentTree<Info> seg(bk);

    for (int i = 0; i < q; i ++ ) {
        int l, r;
        std::cin >> l >> r;
        if (l == r) {
            std::cout << 0 << '\n';
        }else {
            r ++;
            i64 val = seg.rangeQuery(l + 1, r).max;
            std::cout << std::max(0LL, val + pre[l - 1]) << '\n';
        }
    }

    return 0;
}

F题:一起找神秘的数!

思路:

比较显然,如果选定了 x,那么 y 要等于 x。

可以打表去发现,或者直接猜)

代码:

#include <bits/stdc++.h>
using i64 = long long;

void Solve() {
    i64 l, r;
    std::cin >> l >> r;

    std::cout << r - l + 1 << '\n';
}

int main() {
    std::ios::sync_with_stdio(false);
    std::cin.tie(nullptr);
    int tt; std::cin >> tt;
    while (tt --) Solve();
    return 0;
}

G题:一起铸最好的剑!

思路:

幂次增长很快,特判

代码:

#include <bits/stdc++.h>
using i64 = long long;

void Solve() {
    int n, m;
    std::cin >> n >> m;

    if (m >= n) {
        std::cout << 1 << '\n';
        return;
    }

    if (m == 1) {
        std::cout << 1 << '\n';
        return;
    }

    i64 base = 1, d = n - m, ans = 1;
    for (int i = 1; base <= n; i ++ ) {
        base *= m;
        if (base >= 2LL * n - m) {
            break;
        }
        if (abs(base - n) < d) {
            d = abs(base - n);
            ans = i;
        }
    }

    std::cout << ans << '\n';
    
}

int main() {
    std::ios::sync_with_stdio(false);
    std::cin.tie(nullptr);
    int tt; std::cin >> tt;
    while (tt --) Solve();
    return 0;
}

H题:一起画很大的圆!

思路:

感性的题。

如果三点共线,其实是画了一个无穷大的圆。

我们放两个点在一条边上,另外一个点距离这条边越近越好。

感性来说,两个点在长边距离第三个点远一点恰当。

代码:

#include <bits/stdc++.h>
using i64 = long long;

void Solve() {
    int  a, b, c, d;
    std::cin >> a >> b >> c >> d;

    if (b - a > d - c) {
        std::cout << a << ' ' << c << '\n';
        std::cout << a + 1 << ' ' << c << '\n';
        std::cout << b << ' ' << c + 1 << '\n';
    }else {
        std::cout << a << ' ' << c << '\n';
        std::cout << a << ' ' << c + 1 << '\n';
        std::cout << a + 1 << ' ' << d << '\n';
    }

}
int main() {
    std::ios::sync_with_stdio(false);
    std::cin.tie(nullptr);
    int tt; std::cin >> tt;
    while (tt --) Solve();
    return 0;
}

I题:一起看很美的日落!

思路:

拆位。

二进制下每位独立,且只有 0/1,极大地方便了操作。

那么题目就转变成了由 0/1 权值构成的数的全部连通块的权值和。

下面所说的仅考虑第 k 位。

考虑 dp,我们记录经过 u 但是不向上的所有连通块的答案,

因为是异或,我们再记录经过 u 但是不向上的所有连通块 0/1 数量的总和。

还有其所有的连通块数量。

当回溯到 u 节点的时候,可以将 u 本身看作一个块,其连通块数量为 1,0/1 数量取决于第 k 位为 0 还是 1。

将其与其儿子一一进行 操作,就可以算出 了。

具体转移先考虑自身在两个块合并之后的贡献(乘以另一个块中连通块的数量),再加上两者合并中产生的贡献(两者所有连通块 0/1 数量总和的相互乘积)。

还要注意应输出答案 x2。

实现借鉴了 pyy 的代码。

代码:

#include <bits/stdc++.h>
using i64 = long long;

template<class T>
constexpr T power(T a, i64 b) {
    T res = 1;
    for (; b; b /= 2, a *= a) {
        if (b % 2) {
            res *= a;
        }
    }
    return res;
}

constexpr i64 mul(i64 a, i64 b, i64 p) {
    i64 res = a * b - i64(1.L * a * b / p) * p;
    res %= p;
    if (res < 0) {
        res += p;
    }
    return res;
}

template<int P>
struct MInt {
    int x;
    constexpr MInt() : x{} {}
    constexpr MInt(i64 x) : x{norm(x % getMod())} {}
    
    static int Mod;
    constexpr static int getMod() {
        if (P > 0) {
            return P;
        } else {
            return Mod;
        }
    }
    constexpr static void setMod(int Mod_) {
        Mod = Mod_;
    }
    constexpr int norm(int x) const {
        if (x < 0) {
            x += getMod();
        }
        if (x >= getMod()) {
            x -= getMod();
        }
        return x;
    }
    constexpr int val() const {
        return x;
    }
    explicit constexpr operator int() const {
        return x;
    }
    constexpr MInt operator-() const {
        MInt res;
        res.x = norm(getMod() - x);
        return res;
    }
    constexpr MInt inv() const {
        assert(x != 0);
        return power(*this, getMod() - 2);
    }
    constexpr MInt &operator*=(MInt rhs) & {
        x = 1LL * x * rhs.x % getMod();
        return *this;
    }
    constexpr MInt &operator+=(MInt rhs) & {
        x = norm(x + rhs.x);
        return *this;
    }
    constexpr MInt &operator-=(MInt rhs) & {
        x = norm(x - rhs.x);
        return *this;
    }
    constexpr MInt &operator/=(MInt rhs) & {
        return *this *= rhs.inv();
    }
    friend constexpr MInt operator*(MInt lhs, MInt rhs) {
        MInt res = lhs;
        res *= rhs;
        return res;
    }
    friend constexpr MInt operator+(MInt lhs, MInt rhs) {
        MInt res = lhs;
        res += rhs;
        return res;
    }
    friend constexpr MInt operator-(MInt lhs, MInt rhs) {
        MInt res = lhs;
        res -= rhs;
        return res;
    }
    friend constexpr MInt operator/(MInt lhs, MInt rhs) {
        MInt res = lhs;
        res /= rhs;
        return res;
    }
    friend constexpr std::istream &operator>>(std::istream &is, MInt &a) {
        i64 v;
        is >> v;
        a = MInt(v);
        return is;
    }
    friend constexpr std::ostream &operator<<(std::ostream &os, const MInt &a) {
        return os << a.val();
    }
    friend constexpr bool operator==(MInt lhs, MInt rhs) {
        return lhs.val() == rhs.val();
    }
    friend constexpr bool operator!=(MInt lhs, MInt rhs) {
        return lhs.val() != rhs.val();
    }
};

constexpr int P = 1000000007;
using Z = MInt<P>;

int main() {
    std::ios::sync_with_stdio(false);
    std::cin.tie(nullptr);
    
    int n;
    std::cin >> n;

    std::vector<int> a(n);
    for (int i = 0; i < n; i ++ ) {
        std::cin >> a[i];
    }

    std::vector<std::vector<int>> g(n);
    for (int i = 1; i < n; i ++ ) {
        int u, v;
        std::cin >> u >> v;
        u --, v --;

        g[u].push_back(v);
        g[v].push_back(u);
    }
    
    struct Node {
        Z c0[30], c1[30], c, ans;

        Node () {
            for (int i = 0; i < 30; i ++ ) {
                c0[i] = c1[i] = 0;
            }

            c = 0;
            ans = 0;
        }
    };

    auto merge = [&](const Node &u, const Node &v)  {
        Node res = u;
        for (int i = 0; i < 30; i ++ ) {
            res.c0[i] += u.c0[i] * v.c + u.c * v.c0[i];
            res.c1[i] += u.c1[i] * v.c + u.c * v.c1[i];
        }

        res.c += u.c * v.c;
        res.ans += u.ans * v.c + u.c * v.ans;
        for (int i = 0; i < 30; i ++ ) {
            res.ans += (u.c0[i] * v.c1[i] + u.c1[i] * v.c0[i]) * (1LL << i);
        }
        
        return res;
    };

    Z ans = 0;

    auto dfs = [&](auto &&dfs, int u, int f) -> Node {
        Node now = Node();
        now.c = 1;
        now.ans = 0;

        for (int i = 0; i < 30; i ++ ) {
            if (a[u] >> i & 1) {
                now.c1[i] = 1;
            }else {
                now.c0[i] = 1;
            }
        }

        for (auto v : g[u]) {
            if (v == f) continue;
            now = merge(now, dfs(dfs, v, u));
        }

        ans += now.ans;

        return now;
    };

    dfs(dfs, 0, -1);
    
    std::cout << ans * 2 << '\n';

    return 0;
}

J题:数据时间?

思路:

比较经典的想法是,将所有时间转化成秒。

代码:

#include <bits/stdc++.h>
using i64 = long long;
using i128 = __int128;

int main() {
    std::ios::sync_with_stdio(false);
    std::cin.tie(nullptr);
    
    int n, h, m;
    std::cin >> n >> h >> m;
    
    std::set<std::string> a[3] = {};
    auto to = [&](std::string &s, std::string &id) {
        int res = 0;
        res += ((s[0] - '0') * 10 + (s[1] - '0')) * 3600;
        res += ((s[3] - '0') * 10 + (s[4] - '0')) * 60;
        res += ((s[6] - '0') * 10 + (s[7] - '0')) * 1;

        if (res >= 7 * 3600 && res <= 9 * 3600 || res >= 18 * 3600 && res <= 20 * 3600) {
            a[0].insert(id);
        }else if (res >= 11 * 3600 && res <= 13 * 3600) {
            a[1].insert(id);
        }else if (res >= 22 * 3600 || res <= 1 * 3600) {
            a[2].insert(id);
        }
    };

    for (int i = 0; i < n; i ++ ) {
        std::string id, y, t;
        std::cin >> id >> y >> t;
        
        if (std::stoi(y.substr(0, 4)) == h && std::stoi(y.substr(5, 2)) == m) {
            to(t, id);
        }
    }

    for (int i = 0; i < 3; i ++ ) {
        std::cout << int(a[i].size()) << " \n"[i == 2];
    }

    return 0;
}

K题:可以分开吗?

思路:

bfs。

代码:

#include <bits/stdc++.h>
using i64 = long long;

int main() {
    std::ios::sync_with_stdio(false);
    std::cin.tie(nullptr);
    
    int n, m;
    std::cin >> n >> m;

    std::vector<std::vector<int>> a(n + 1, std::vector<int>(m + 1));
    for (int i = 1; i <= n; i ++ ) {
        for (int j = 1; j <= m; j ++ ) {
            char c;
            std::cin >> c;
            a[i][j] = c == '1';
        }
    }

    std::vector<std::vector<int>> vis(n + 1, std::vector<int>(m + 1));
    std::vector<int> dx = {-1, 1, 0, 0}, dy = {0, 0, -1, 1};

    int ans = 1E9 + 7;
    auto work = [&](int x, int y) {
        std::set<std::pair<int, int>> p;
        vis[x][y] = 1;
        std::queue<std::pair<int, int>> q;
        q.push({x, y});
        
        while (q.size()) {
            auto [xn, yn] = q.front();
            q.pop();
            for (int i = 0; i < 4; i ++ ) {
                int xt = xn + dx[i], yt = yn + dy[i];
                if (xt < 1 || xt > n || yt < 1 || yt > m) continue;
                if (vis[xt][yt]) continue;
                if (!a[xt][yt]) {
                    p.insert({xt, yt});
                    continue;
                }
                
                vis[xt][yt] = 1;
                q.push({xt, yt});
            }
        }
        ans = std::min(ans, int(p.size()));
    };

    for (int i = 1; i <= n; i ++ ) {
        for (int j = 1; j <= m; j ++ ) {
            if (a[i][j] && !vis[i][j]) {
                work(i, j);
            }
        }
    }

    std::cout << ans << '\n';
    
    return 0;
}

L题:还会再见吗?

思路:

虚树 + 最短路。

首先仅考虑单种颜色的情况,若此点是关键点,要有颜色重复,我们还需要去找一个关键点。

另一个关键点的求法使用 dp。

我们将所有的关键点求出答案后,使用最短路算法就可以求出所有点的答案了。

此题有很多种颜色。使用虚树可以压缩信息。

就变成了板子题?)

代码:

#include <bits/stdc++.h>
using i64 = long long;

struct HLD {
    int n;
    std::vector<int> siz, top, dep, parent, in, out, seq;
    std::vector<std::vector<int>> adj;
    int cur;
    
    HLD() {}
    HLD(int n) {
        init(n);
    }
    void init(int n) {
        this->n = n;
        siz.resize(n);
        top.resize(n);
        dep.resize(n);
        parent.resize(n);
        in.resize(n);
        out.resize(n);
        seq.resize(n);
        cur = 0;
        adj.assign(n, {});
    }
    void addEdge(int u, int v) {
        adj[u].push_back(v);
        adj[v].push_back(u);
    }
    void work(int root = 0) {
        top[root] = root;
        dep[root] = 0;
        parent[root] = -1;
        dfs1(root);
        dfs2(root);
    }
    void dfs1(int u) {
        if (parent[u] != -1) {
            adj[u].erase(std::find(adj[u].begin(), adj[u].end(), parent[u]));
        }
        
        siz[u] = 1;
        for (auto &v : adj[u]) {
            parent[v] = u;
            dep[v] = dep[u] + 1;
            dfs1(v);
            siz[u] += siz[v];
            if (siz[v] > siz[adj[u][0]]) {
                std::swap(v, adj[u][0]);
            }
        }
    }
    void dfs2(int u) {
        in[u] = cur++;
        seq[in[u]] = u;
        for (auto v : adj[u]) {
            top[v] = v == adj[u][0] ? top[u] : v;
            dfs2(v);
        }
        out[u] = cur;
    }
    int lca(int u, int v) {
        while (top[u] != top[v]) {
            if (dep[top[u]] > dep[top[v]]) {
                u = parent[top[u]];
            } else {
                v = parent[top[v]];
            }
        }
        return dep[u] < dep[v] ? u : v;
    }
    
    int dist(int u, int v) {
        return dep[u] + dep[v] - 2 * dep[lca(u, v)];
    }
    
    int jump(int u, int k) {
        if (dep[u] < k) {
            return -1;
        }
        
        int d = dep[u] - k;
        
        while (dep[top[u]] > d) {
            u = parent[top[u]];
        }
        
        return seq[in[u] - dep[u] + d];
    }
    
    bool isAncester(int u, int v) {
        return in[u] <= in[v] && in[v] < out[u];
    }
    
    int rootedParent(int u, int v) {
        std::swap(u, v);
        if (u == v) {
            return u;
        }
        if (!isAncester(u, v)) {
            return parent[u];
        }
        auto it = std::upper_bound(adj[u].begin(), adj[u].end(), v, [&](int x, int y) {
            return in[x] < in[y];
        }) - 1;
        return *it;
    }
    
    int rootedSize(int u, int v) {
        if (u == v) {
            return n;
        }
        if (!isAncester(v, u)) {
            return siz[v];
        }
        return n - siz[rootedParent(u, v)];
    }
    
    int rootedLca(int a, int b, int c) {
        return lca(a, b) ^ lca(b, c) ^ lca(c, a);
    }
};

constexpr int Iinf = 1E9 + 7;

int main() {
    std::ios::sync_with_stdio(false);
    std::cin.tie(nullptr);
    
    int n, q;
    std::cin >> n >> q;

    std::vector<int> ans(n, Iinf);
    std::map<int, std::vector<int>> col_;
    for (int i = 0; i < n; i ++ ) {
        int a;
        std::cin >> a;
        
        std::set<int> s;
        for (int j = 0; j < a; j ++ ) {
            int c;
            std::cin >> c;

            if (s.count(c)) {
                ans[i] = 0;
            }else {
                col_[c].push_back(i);
            }
            s.insert(c);
        }
    }

    HLD g(n);
    std::vector<std::vector<int>> adj(n);
    for (int i = 1; i < n; i ++ ) {
        int u, v;
        std::cin >> u >> v;
        u --, v --;

        g.addEdge(u, v);
        adj[u].push_back(v);
        adj[v].push_back(u);
    }

    g.work();

    std::vector<std::vector<int>> vir(n);
    std::vector<int> isvir(n), d1(n), d2(n);
    
    for (auto &[_, h] : col_) {
        for (auto &v : h) {
            isvir[v] = 1;
        }

        std::sort(h.begin(), h.end(), [&](int &i, int &j) {
            return g.in[i] < g.in[j];
        });

        std::vector<int> stk = {0};
        int top = 0;
        vir[0].clear();
        
        for (auto &v : h) {
            if (v == 0) continue;
            
            int lca = g.lca(v, stk[top]);
            if (lca != stk[top]) {
                while (g.in[lca] < g.in[stk[top - 1]]) {
                    vir[stk[top - 1]].push_back(stk[top]);
                    top --;
                    stk.pop_back();
                }

                if (g.in[lca] != g.in[stk[top - 1]]) {
                    vir[lca].clear();
                    vir[lca].push_back(stk[top]);
                    stk[top] = lca;
                }else {
                    vir[lca].push_back(stk[top]);
                    top --;
                    stk.pop_back();
                }
            }

            vir[v].clear();
            top ++;
            stk.push_back(v);
        }

        for (int i = 0; i < top; i ++ ) {
            vir[stk[i]].push_back(stk[i + 1]);
        }

        auto dfs1 = [&](auto &&dfs1, int u) -> void {
            d1[u] = Iinf;
            for (auto &v : vir[u]) {
                dfs1(dfs1, v);
                d1[u] = std::min(d1[u], d1[v] + g.dep[v] - g.dep[u]);
            }

            d2[u] = d1[u];
            if (isvir[u]) d1[u] = 0;
        };

        dfs1(dfs1, 0);

        auto dfs2 = [&](auto &&dfs2, int u, int up = Iinf) -> void {
            if (isvir[u]) {
                ans[u] = std::min({ans[u], d2[u], up});
                up = 0;
            }

            int min1 = up, min2 = Iinf;
            for (auto &v : vir[u]) {
                int min = d1[v] + g.dep[v] - g.dep[u];
                if (min <= min1) {
                    min2 = min1;
                    min1 = min;
                }else if (min < min2) {
                    min2 = min;
                }
            }

            for (auto &v : vir[u]) {
                int min = d1[v] + g.dep[v] - g.dep[u];

                if (min == min1) {
                    dfs2(dfs2, v, min2 + g.dep[v] - g.dep[u]);
                }else {
                    dfs2(dfs2, v, min1 + g.dep[v] - g.dep[u]);
                }
            }

            isvir[u] = 0;
        };

        dfs2(dfs2, 0);
    }
    
    using P = std::pair<int, int>;
    std::priority_queue<P, std::vector<P>, std::greater<P>> pq;
    for (int i = 0; i < n; i ++ ) {
        pq.push({ans[i], i});
    }

    std::vector<int> vis(n);
    while (pq.size()) {
        auto u = pq.top().second;
        pq.pop();

        if (vis[u]) continue;
        vis[u] = 1;
        for (auto &v : adj[u]) {
            if (ans[v] > ans[u] + 1) {
                ans[v] = ans[u] + 1;
                pq.push({ans[v], v});
            }
        }
    }

    for (int i = 0; i < q; i ++ ) {
        int x;
        std::cin >> x;
        x --;

        if (ans[x] == Iinf) {
            std::cout << "IMPOSSIBLE!\n";
        }else {
            std::cout << ans[x] << '\n';
        }
    }

    return 0;
}

M题:那是我们的影子

思路:

性质题。

我们可以发现,位置 %3 后出现的元素应该是相同的。

具体来说。

1??2
2??1
3??3

对于第四列,它使用的元素只能是 {1, 2, 3}。

那么其实我们确定了前三列后,每列上具体元素是啥其实已经确定了。

记该列上有 个 ?,这列上能提供的不同方案数为

所以,我们要去确定前三列到底是啥。

具体来说,我们先去确定前三列到底已经确定了哪些数。

这个确定指的是。

???1
???2
???4

这个看起来第一列全是 ?,但是其实已经被确定为 {1, 2, 4}了。

我们将未被确认的数,选入前三列的每一列,使其确认的数达到 3 个。

比如说,我们得到的是。

???
???
???

我们实际上是,

再来一个例子。

???1
????
????

我们实际上是,

然后就是要注意输出 0 的情况了。

有下列三种情况。

1.该列上有相同的元素

1??
1??
???

2.前三列中确认的元素超过了 3 个

1??2
3??4
????

3.前三列中确认的元素有重复

11?
???
???

代码:

#include <bits/stdc++.h>
using i64 = long long;

template<class T>
constexpr T power(T a, i64 b) {
    T res = 1;
    for (; b; b /= 2, a *= a) {
        if (b % 2) {
            res *= a;
        }
    }
    return res;
}

constexpr i64 mul(i64 a, i64 b, i64 p) {
    i64 res = a * b - i64(1.L * a * b / p) * p;
    res %= p;
    if (res < 0) {
        res += p;
    }
    return res;
}

template<int P>
struct MInt {
    int x;
    constexpr MInt() : x{} {}
    constexpr MInt(i64 x) : x{norm(x % getMod())} {}
    
    static int Mod;
    constexpr static int getMod() {
        if (P > 0) {
            return P;
        } else {
            return Mod;
        }
    }
    constexpr static void setMod(int Mod_) {
        Mod = Mod_;
    }
    constexpr int norm(int x) const {
        if (x < 0) {
            x += getMod();
        }
        if (x >= getMod()) {
            x -= getMod();
        }
        return x;
    }
    constexpr int val() const {
        return x;
    }
    explicit constexpr operator int() const {
        return x;
    }
    constexpr MInt operator-() const {
        MInt res;
        res.x = norm(getMod() - x);
        return res;
    }
    constexpr MInt inv() const {
        assert(x != 0);
        return power(*this, getMod() - 2);
    }
    constexpr MInt &operator*=(MInt rhs) & {
        x = 1LL * x * rhs.x % getMod();
        return *this;
    }
    constexpr MInt &operator+=(MInt rhs) & {
        x = norm(x + rhs.x);
        return *this;
    }
    constexpr MInt &operator-=(MInt rhs) & {
        x = norm(x - rhs.x);
        return *this;
    }
    constexpr MInt &operator/=(MInt rhs) & {
        return *this *= rhs.inv();
    }
    friend constexpr MInt operator*(MInt lhs, MInt rhs) {
        MInt res = lhs;
        res *= rhs;
        return res;
    }
    friend constexpr MInt operator+(MInt lhs, MInt rhs) {
        MInt res = lhs;
        res += rhs;
        return res;
    }
    friend constexpr MInt operator-(MInt lhs, MInt rhs) {
        MInt res = lhs;
        res -= rhs;
        return res;
    }
    friend constexpr MInt operator/(MInt lhs, MInt rhs) {
        MInt res = lhs;
        res /= rhs;
        return res;
    }
    friend constexpr std::istream &operator>>(std::istream &is, MInt &a) {
        i64 v;
        is >> v;
        a = MInt(v);
        return is;
    }
    friend constexpr std::ostream &operator<<(std::ostream &os, const MInt &a) {
        return os << a.val();
    }
    friend constexpr bool operator==(MInt lhs, MInt rhs) {
        return lhs.val() == rhs.val();
    }
    friend constexpr bool operator!=(MInt lhs, MInt rhs) {
        return lhs.val() != rhs.val();
    }
};

constexpr int P = 1000000007;
using Z = MInt<P>;

int C (int a, int b) {
    int res = 1;
    for (int i = a; i >= a - b + 1; i -- ) {
        res *= i;
    }
    for (int i = 1; i <= b; i ++ ) {
        res /= i;
    }
    return res;
}

void Solve() {
    int n;
    std::cin >> n;

    std::vector<std::vector<char>> a(3, std::vector<char>(n));
    std::set<int> s[3] = {};

    for (int i = 0; i < 3; i ++ ) {
        for (int j = 0; j < n; j ++ ) {
            std::cin >> a[i][j];
        }
    }
    
    for (int i = 0; i < 3; i ++ ) {
        for (int j = 0; j < n; j ++ ) {
            if (a[i][j] != '?') {
                s[j % 3].insert(a[i][j] - '0');
            }
        }
    }

    for (int i = 0; i < 3; i ++ ) {
        if (int(s[i].size()) > 3) {
            std::cout << 0 << '\n';
            return;
        }
    }

    std::vector<int> use(10);
    for (int i = 0; i < 3; i ++ ) {
        for (auto v : s[i]) {
            if (use[v]) {
                std::cout << 0 << '\n';
                return;
            }
            use[v] = 1;
        }
    }

    for (int i = 0; i < n; i ++ ) {
        std::set<int> s;
        for (int j = 0; j < 3; j ++ ) {
            if (a[j][i] != '?') {
                if (s.count(a[j][i] - '0')) {
                    std::cout << 0 << '\n';
                    return;
                }
                s.insert(a[j][i] - '0');
            }
        }
    }

    int cnt = 0;
    for (int i = 0; i < 3; i ++ ) {
        cnt += 3 - int(s[i].size());
    }

    Z ans = 1;
    for (int i = 0; i < 3; i ++ ) {
        ans *= C(cnt, 3 - int(s[i].size()));
        cnt -= 3 - int(s[i].size());
    }

    for (int i = 0; i < n; i ++ ) {
        int cnt = 0;
        for (int j = 0; j < 3; j ++ ) {
            if (a[j][i] == '?') {
                ans *= ++cnt;
            }
        }
    }

    std::cout << ans << '\n';

}

int main() {
    std::ios::sync_with_stdio(false);
    std::cin.tie(nullptr);
    int tt; std::cin >> tt;
    while (tt --) Solve();
    return 0;
}