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 字符串游戏
太菜了

浙公网安备 33010602011771号