「国家集训队 2011 扬天」解题报告

A – 等差子序列

因为是个排列,读入时我们可以把已有的数值放在数轴上,发现只要有一个对称即可。我们可以用哈希的思想魔改一下树状数组,维护串和反串的哈希然后判断即可。

// FOJ1913.cpp
#include <bits/stdc++.h>

using namespace std;

const int MAX_N = 10100, bitnum = 133;
typedef unsigned long long ull;

int T, n, ai[MAX_N];
ull nodes[2][MAX_N], pw[MAX_N];

inline char nc()
{
    static char buf[1000000], *p1 = buf, *p2 = buf;
    return p1 == p2 && (p2 = (p1 = buf) + fread(buf, 1, 1000000, stdin), p1 == p2) ? EOF : *p1++;
}

int read()
{
    int x = 0, f = 1;
    char ch = nc();
    while (!isdigit(ch))
        ch = nc();
    while (isdigit(ch))
        x = (x << 3) + (x << 1) + ch - '0', ch = nc();
    return x * f;
}

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

void update(ull *arr, int x)
{
    for (int i = x; i <= n; i += lowbit(i))
        arr[i] += pw[i - x];
}

ull query(ull *arr, int x, ull ret = 0)
{
    for (int i = x; i; i -= lowbit(i))
        ret += arr[i] * pw[x - i];
    return ret;
}

ull query(ull *arr, int l, int r) { return query(arr, r) - query(arr, l - 1) * pw[r - l + 1]; }

int main()
{
    T = read();
    for (int i = pw[0] = 1; i < MAX_N; i++)
        pw[i] = pw[i - 1] * bitnum;
    while (T--)
    {
        memset(nodes, 0, sizeof(nodes));
        n = read();
        bool flag = false;
        for (int i = 1; i <= n; i++)
        {
            ai[i] = read();
            int len = min(ai[i] - 1, n - ai[i]);
            if (len && query(nodes[0], ai[i] - len, ai[i]) != query(nodes[1], n - ai[i] - len + 1, n - ai[i] + 1))
                flag = true;
            update(nodes[0], ai[i]), update(nodes[1], n - ai[i] + 1);
        }
        puts(flag ? "Y" : "N");
    }
    return 0;
}

B – 最短路

我们考虑一个常见套路:也就是把环缩起来变成一棵树,然后用树上差分的套路来做。当然,我们不能直接硬算,因为假设 \(LCA\) 为一个环点,那么统计环上的一段弧长就比较麻烦。我们考虑先求出从\(1\)号点的单源最段路,然后再统计每个环点的环长,可知环上两点\(A, B\)的距离为:\(\min \{ |pre[A] – pre[B]|, loop – |pre[A] – pre[B]| \} \)。其中,\(pre\)是新树上的距离,新树为原图暴力破环(且随机)后的图。

// FOJ1914.cpp
#include <bits/stdc++.h>

using namespace std;

const int MAX_N = 1e5 + 200;
typedef pair<int, int> pii;
typedef long long ll;

int head[MAX_N], current, n, m, q, dfn[MAX_N], low[MAX_N], stk[MAX_N], tail, ptot;
int ncnt, aff[MAX_N], up[15][MAX_N], dep[MAX_N];
ll dist[MAX_N], loops[MAX_N], prefix[MAX_N];
bool inst[MAX_N], vis[MAX_N];

vector<int> G[MAX_N];

struct edge
{
    int to, nxt, weight;
} edges[MAX_N << 1];

void addpath(int src, int dst, int weight)
{
    edges[current].to = dst, edges[current].nxt = head[src];
    edges[current].weight = weight, head[src] = current++;
}

void shortest_path(int src)
{
    memset(dist, 0x3f, sizeof(dist));
    priority_queue<pii> pq;
    pq.push(make_pair(0, src)), dist[src] = 0;
    while (!pq.empty())
    {
        int u = pq.top().second;
        pq.pop();
        if (vis[u])
            continue;
        vis[u] = true;
        for (int i = head[u]; i != -1; i = edges[i].nxt)
            if (dist[edges[i].to] > dist[u] + edges[i].weight)
                dist[edges[i].to] = dist[u] + edges[i].weight, pq.push(make_pair(-dist[edges[i].to], edges[i].to));
    }
    memset(vis, false, sizeof(vis));
}

void tarjan(int u, int fa)
{
    dfn[u] = ++ptot;
    for (int i = head[u]; i != -1; i = edges[i].nxt)
        if (edges[i].to != fa)
        {
            if (dfn[edges[i].to] == 0)
                up[0][edges[i].to] = u, prefix[edges[i].to] = prefix[u] + edges[i].weight, tarjan(edges[i].to, u);
            else if (dfn[edges[i].to] < dfn[u])
            {
                loops[++ncnt] = 0LL + prefix[u] - prefix[edges[i].to] + edges[i].weight;
                for (int pt = u, pre; pt != edges[i].to;)
                    G[edges[i].to].push_back(pt), aff[pt] = ncnt, pre = up[0][pt], up[0][pt] = edges[i].to, pt = pre;
            }
        }
    if (up[0][u] == fa)
        G[fa].push_back(u);
}

void dfs_collect(int u, int fa)
{
    dep[u] = dep[fa] + 1;
    for (int i = 0, siz = G[u].size(); i < siz; i++)
        if (G[u][i] != fa)
            dfs_collect(G[u][i], u);
}

ll query(int x, int y)
{
    if (dep[x] < dep[y])
        swap(x, y);
    int u = x, v = y;
    for (int i = 14; i >= 0; i--)
        if (dep[up[i][x]] >= dep[y])
            x = up[i][x];
    if (x == y)
        return dist[u] - dist[v];
    for (int i = 14; i >= 0; i--)
        if (up[i][x] != up[i][y])
            x = up[i][x], y = up[i][y];
    if (aff[x] && aff[x] == aff[y])
        return dist[u] - dist[x] + dist[v] - dist[y] + min(llabs(prefix[x] - prefix[y]), loops[aff[x]] - llabs(prefix[x] - prefix[y]));
    return dist[u] + dist[v] - (dist[up[0][x]] << 1);
}

int main()
{
    memset(head, -1, sizeof(head));
    scanf("%d%d%d", &n, &m, &q);
    for (int i = 1, u, v, w; i <= m; i++)
        scanf("%d%d%d", &u, &v, &w), addpath(u, v, w), addpath(v, u, w);
    shortest_path(1), tarjan(1, 0);
    dfs_collect(1, 0);
    for (int i = 1; i <= 14; i++)
        for (int j = 1; j <= ptot; j++)
            up[i][j] = up[i - 1][up[i - 1][j]];
    while (q--)
    {
        int x, y;
        scanf("%d%d", &x, &y);
        printf("%lld\n", query(x, y));
    }
    return 0;
}

C – 排斥反应

首先这个问题可以转换成在一个表格上做互不侵犯。具体转换的方式:左上角为起点,向左走视为\(+p\),向右走视为\(+q\),因为\(\gcd(p, q) = 1\),所以表格上的数字各不相同。转换为这个问题之后,我们就可以设计一个状压 DP 的方程。发现状态必须满足两个一不相邻且前端、末端的\(1\)不相邻,所以当\(p = 10\)时,状态变少为\(123\)种。可以考虑矩阵快速幂。

// FOJ1915.cpp
#include <bits/stdc++.h>

using namespace std;

const int MAX_N = 150, mod = 19921107;

int p, q, stats[MAX_N], stat_tot;

struct matrix
{
    int mat[MAX_N][MAX_N];

    void clear() { memset(mat, 0, sizeof(mat)); }

    int *operator[](const int &rhs) { return mat[rhs]; }

    matrix operator*(const matrix &rhs)
    {
        matrix ret;
        ret.clear();
        for (int i = 1; i <= stat_tot; i++)
            for (int j = 1; j <= stat_tot; j++)
                for (int k = 1; k <= stat_tot; k++)
                    ret[i][j] = (1LL * ret[i][j] + 1LL * mat[i][k] * rhs.mat[k][j] % mod) % mod;
        return ret;
    }

    matrix operator^(const int &rhs)
    {
        int tim = rhs;
        matrix ret, bas = *this;
        ret.clear();
        for (int i = 1; i <= stat_tot; i++)
            ret[i][i] = 1;
        while (tim)
        {
            if (tim & 1)
                ret = ret * bas;
            bas = bas * bas;
            tim >>= 1;
        }
        return ret;
    }
} A, trans;

int main()
{
    scanf("%d%d", &p, &q);
    // process the stats;
    for (int i = 0; i < (1 << p); i++)
    {
        bool flag = true;
        for (int j = 1; j < p; j++)
            if ((i & (1 << (j - 1))) && (i & (1 << j)))
            {
                flag = false;
                break;
            }
        if (p != 1)
            flag &= (!((i & 1) && (i & (1 << (p - 1)))));
        if (flag)
            stats[++stat_tot] = i;
    }
    if (q == 1)
        printf("%d\n", stat_tot), exit(0);
    int ans = 0;
    trans.clear();
    for (int i = 1; i <= stat_tot; i++)
    {
        A[i][i] = 1;
        for (int j = 1; j <= stat_tot; j++)
            if (!(stats[i] & stats[j]))
                trans[i][j] = 1;
    }
    A = A * (trans ^ (q - 1));
    for (int i = 1; i <= stat_tot; i++)
        for (int j = 1; j <= stat_tot; j++)
            if (!(stats[i] & stats[j]))
                ans = (1LL * ans + A[i][j]) % mod;
    printf("%d\n", ans);
    return 0;
}

 

Leave a Reply

Your email address will not be published. Required fields are marked *