20251108OIFHA

T1 云之国

赛时未考虑到最大值不能取模,饮恨败北了...

直接顺着题目思路往下想就可以了.

首先考虑询问求的是两个弱连通分量(忽略边的方向的连通块)之间连边,再求最长路径的期望.容易把问题转化成预处理出以这个点为起点的最长路径长度\(dis[x]\)和以这个点为终点的最长路径长度\(en[x]\),再遍历两个弱连通分量内的所有点\(x\),\(y\),再求\(\sum_{x} \sum_{y} min(原最大值,en[x]+dis[y])\),但这样做的时间复杂度为\(O(qn^2)\),但看到有\(min\)操作,于是可以用双指针优化或二分优化(具体见代码).

那么难点在于如何求出\(en[x]\)\(dis[x]\).由于一个强连通分量内的点可以相互到达且可以重复走同一条边,那么一个点先走完它所属的强连通分量一定不劣.于是先缩点得到一个\(DAG\),即可得到\(en[x]\).建反图再做拓扑排序即可得到\(dis[x]\).而原最大值即为\(Max_{dis[x]}\).

注意空间只有16mib,是"Impossib1e."不是"Impossible.",求最大值过程中不能
取模.

代码如下(用双指针还是比二分要快的):

#include <bits/stdc++.h>
using namespace std;
#define int long long

int n, m;
const int mod = 100000007;
int qpow(int xx, int yy) {
    long long ans = 1ll, x = xx, y = yy;
    while (y) {
        if (y % 2ll == 1ll) {
            ans *= x;
            ans %= mod;
        }
        y /= 2;
        x *= x;
        x %= mod;
    }
    return ans;
}
struct QQ {
    int x, y, val, id, v;
} Q[3600];
bool cmp(int x, int y) { return x > y; }
struct E1 {
    int v, nxt, to;
} e[55000], e2[55000], E[155000];
struct EC {
    int x, y, v;
    bool operator<(const EC &h) const {
        if (x == h.x) {
            if (y == h.y) {
                return v > h.v;
            }
            return y < h.y;
        }
        return x < h.x;
    }
    bool operator==(const EC &h) const { return (x == h.x) && (y == h.y); }
} E1[155000];
int t1, t2;
int head[3600], cnt;
int head2[3600], cnt2;
int Head[3600], Cnt;
void add(int x, int y, int v) {
    cnt++;
    e[cnt].to = y;
    e[cnt].v = v;
    e[cnt].nxt = head[x];
    head[x] = cnt;
}
void add3(int x, int y, int v) {
    cnt2++;
    e2[cnt2].to = y;
    e2[cnt2].v = v;
    e2[cnt2].nxt = head2[x];
    head2[x] = cnt2;
}
void add2(int x, int y, int v) {
    Cnt++;
    E[Cnt].to = y;
    E[Cnt].v = v;
    E[Cnt].nxt = Head[x];
    Head[x] = Cnt;
}
int col[3600], dfn[3600], low[3600], st[3600], tmp, kind;
int v[3600], dis[3600], en[3600];
deque<int> q;
// e
void dfs(int x) {
    dfn[x] = low[x] = ++tmp;
    st[x] = 1;
    q.push_back(x);
    for (int i = head[x]; i; i = e[i].nxt) {
        int to = e[i].to;
        if (!dfn[to]) {
            dfs(to);
            low[x] = min(low[x], low[to]);
        } else if (st[to]) {
            low[x] = min(low[x], dfn[to]);
        }
    }
    if (dfn[x] == low[x]) {
        ++kind;
        while (!q.empty()) {
            int t = q.back();
            q.pop_back();
            st[t] = 0;
            col[t] = kind;
            if (t == x) {
                break;
            }
        }
    }
}
// e2
int ru[3600];
queue<int> d;
void tp1() {
    while (!d.empty()) d.pop();
    for (int i = 1; i <= kind; i++) {
        if (ru[i] == 0)
            d.push(i);
    }
    while (!d.empty()) {
        int tp = d.front();
        d.pop();
        for (int i = head2[tp]; i; i = e2[i].nxt) {
            int to = e2[i].to;
            en[to] = max(en[to], en[tp] + e2[i].v + v[tp]);
            ru[to]--;
            if (!ru[to])
                d.push(to);
        }
    }
}
void tp2() {
    while (!d.empty()) d.pop();
    for (int i = 1; i <= kind; i++) {
        if (ru[i] == 0)
            d.push(i);
    }
    while (!d.empty()) {
        int tp = d.front();
        d.pop();
        for (int i = head2[tp]; i; i = e2[i].nxt) {
            int to = e2[i].to;
            dis[to] = max(dis[to], dis[tp] + e2[i].v + v[tp]);
            ru[to]--;
            if (!ru[to])
                d.push(to);
        }
    }
}
int vis[3600], op;
vector<int> s[3600][2];
// E
void li(int x) {
    vis[x] = op;
    s[op][0].push_back(en[col[x]]);
    s[op][1].push_back(dis[col[x]]);
    for (int i = Head[x]; i; i = E[i].nxt) {
        int to = E[i].to;
        if (!vis[to]) {
            li(to);
        }
    }
}
int an = 0;
int query(int x, int y, int va) {
    int sx = s[x][0].size() - 1, sy = s[y][1].size() - 1;
    int num = ((sx + 1) * (sy + 1)) % mod, t = 0, sum = 0, su = 0;
    for (int i = 0; i <= sx; i++) {
        while (s[y][1][t] + s[x][0][i] + va > an && t <= sy) {
            su += s[y][1][t];
            su %= mod;
            t++;
        }
        if (t == sy && s[y][1][t] + s[x][0][i] + va > an) {
            sum += (su + ((sy + 1) * (s[x][0][i] + va) % mod) % mod) % mod;
        } else {
            sum += ((sy - t + 1) * (an % mod)) % mod + (su + (t * (s[x][0][i] + va) % mod) % mod) % mod;
        }
        sum %= mod;
    }
    return sum * qpow(num, mod - 2) % mod;
}
void init() {
    // edge
    cnt = cnt2 = Cnt = 0;
    memset(head, 0, sizeof(head));
    memset(head2, 0, sizeof(head2));
    memset(Head, 0, sizeof(Head));
    // other
    tmp = 0, kind = 0, op = 0, an = 0, t1 = 0;
    memset(col, 0, sizeof(col));
    memset(dfn, 0, sizeof(dfn));
    memset(low, 0, sizeof(low));
    memset(st, 0, sizeof(st));
    memset(v, 0, sizeof(v));
    memset(dis, 0, sizeof(dis));
    memset(en, 0, sizeof(en));
    memset(vis, 0, sizeof(vis));
    for (int i = 1; i <= 3000; i++) s[i][0].clear(), s[i][1].clear();
    while (!q.empty()) q.pop_front();
}
void sol() {
    scanf("%lld%lld", &n, &m);
    init();
    for (int i = 1; i <= m; i++) {
        int x, y, v;
        scanf("%lld%lld%lld", &x, &y, &v);
        add(x, y, v);
        add2(x, y, v);
        add2(y, x, v);
    }
    for (int i = 1; i <= n; i++) {
        if (!dfn[i]) {
            dfs(i);
        }
    }
    for (int i = 1; i <= n; i++) {
        for (int j = head[i]; j; j = e[j].nxt) {
            int to = e[j].to;
            if (col[to] != col[i]) {
                E1[++t1] = { col[i], col[to], e[j].v };
            } else {
                v[col[i]] += e[j].v;
            }
        }
    }
    sort(E1 + 1, E1 + 1 + t1);
    memset(head2, 0, sizeof(head2));
    memset(ru, 0, sizeof(ru));
    cnt2 = 0;
    for (int i = 1; i <= t1; i++) {
        if (E1[i] == E1[i - 1])
            continue;
        ru[E1[i].y]++;
        add3(E1[i].x, E1[i].y, E1[i].v);
    }
    tp1();
    memset(ru, 0, sizeof(ru));
    memset(head2, 0, sizeof(head2));
    cnt2 = 0;
    for (int i = 1; i <= t1; i++) {
        if (E1[i] == E1[i - 1])
            continue;
        ru[E1[i].x]++;
        add3(E1[i].y, E1[i].x, E1[i].v);
    }
    tp2();
    for (int i = 1; i <= kind; i++) {
        dis[i] += v[i], en[i] += v[i];
        an = max(an, dis[i]);
    }
    for (int i = 1; i <= n; i++) {
        if (!vis[i]) {
            op++;
            li(i);
        }
    }
    for (int i = 1; i <= op; i++) {
        sort(s[i][0].begin(), s[i][0].end());
        sort(s[i][1].begin(), s[i][1].end(), cmp);
    }
    int qnum;
    scanf("%lld", &qnum);
    for (int i = 1; i <= qnum; i++) {
        int x, y, v;
        scanf("%lld%lld%lld", &x, &y, &v);
        if (vis[x] == vis[y]) {
            printf("Impossib1e.\n");
            continue;
        }
        printf("%lld\n", query(vis[x], vis[y], v));
    }
}
signed main() {
    int T;
    scanf("%lld", &T);
    while (T--) {
        sol();
    }
}

T2 重排

先观察变化规律.容易发现KXP将P放到开头就变成了PKX,再把X放到开头就变成了XPK。由此可以得出它们是循环变换的。那么这就有一个很好的的性质:我们设KXP,PKX,XPK为"*** "(因为它们可以互相转换,所以具体是什么不重要),那么*** K/P/X可以变为K/P/X***(可以自行枚举证明)。

解决字符串匹配有两种常见方法,一是从左往右让\(S\)\(T\)一一匹配,匹配完成的部分不去管它。另一种是若操作可逆,则将\(S\)\(T\)变为同一个字符串,即可证明两个字符串是匹配的。

本题用方法一不好处理。注意到操作可逆,且有开头提到的性质,于是思考方法二。我们可以发现,只有*** 是可以随便动的,那我们让所有*** 移到后面,在比较\(S\)\(T\)剩余不可移动部分是否相等,即可得到是否匹配。

那么如何把所有*** 求出来呢?(记得考虑一个*** 移走后两边有可能可以重新拼成一个 *** )。由于*** 长度只有3,那么一种优秀的写法是用栈来维护,从左往右枚举,每次遇到一个数,看一下它和栈顶两个字符能否拼成***,如果可以就把它们弹出栈,否则压入当前字符。

代买实现较为简单,就不贴了。

T3 为了那塔

原神题,太菜了

T4 字符串游戏

太菜了

posted @ 2025-11-11 22:13  H_Fx  阅读(7)  评论(0)    收藏  举报