直接模拟题意即可。依次松弛每个节点的蓄水量,直至到达最终排水口。需要注意的是,在松弛任意一个节点 v 的蓄水量时,需要保证:对于图中的每一条有向边 (u,v),u 的蓄水量都被松弛过了。
发现可以通过拓扑序来转移。
#include<cstdio>#include<cstring>#include<algorithm>#include<queue>#include<vector>#define u128 __int128
using namespace std;
inlinevoidprint(u128 x) {
if (x > 9) print(x / 10);
putchar('0' + x % 10);
}
u128 gcd(u128 a, u128 b) {
if (!b) return a;
return gcd(b, a % b);
}
constint N = 400100;
structNode {
u128 x, y;
} a[N];
Node operator + (Node a, Node b) {
Node c;
c.y = a.y * b.y;
c.x = a.x * b.y + b.x * a.y;
u128 S = gcd(c.x, c.y);
if (!S) {
c.x = 0;
c.y = 1;
} else {
c.x /= S;
c.y /= S;
}
return c;
}
Node operator / (Node a, int num) {
Node b;
b.x = a.x;
b.y = 1ll * a.y * num;
u128 S = gcd(b.x, b.y);
if (!S) {
b.x = 0;
b.y = 1;
} else {
b.x /= S;
b.y /= S;
}
return b;
}
int n, m;
vector<int> to[N];
int deg[N];
voidtopsort() {
queue<int> q;
for (int i = 1; i <= n; i ++)
if (deg[i] == 0) q.push(i);
while (q.size()) {
int u = q.front(); q.pop();
if (to[u].size() == 0) continue;
Node give = a[u] / to[u].size();
for (int i = 0; i < (int)to[u].size(); i ++) {
int v = to[u][i];
a[v] = a[v] + give;
if (-- deg[v] == 0) q.push(v);
}
}
}
intmain() {
scanf("%d%d", &n, &m);
for (int i = 1, S; i <= n; i ++) {
scanf("%d", &S);
while (S --) {
int x;
scanf("%d", &x);
to[i].push_back(x);
}
}
for (int i = 1; i <= m; i ++)
a[i].x = 1, a[i].y = 1;
for (int i = m + 1; i <= n; i ++)
a[i].x = 0, a[i].y = 1;
for (int i = 1; i <= n; i ++)
for (int j = 0; j < (int)to[i].size(); j ++) {
int v = to[i][j];
deg[v] ++;
}
topsort();
for (int i = 1; i <= n; i ++)
if (to[i].size() == 0) {
print(a[i].x);
printf(" ");
print(a[i].y);
puts("");
}
return0;
}
// I hope changle_cyx can pray for me.
此时整个字符串的划分结构就已经是确定的了。F(C) 已经预处理好了,那这种情况对答案的贡献,相当于要在 T 里数出有多少个真前缀A 满足 F(A)≤F(C),那直接用树状数组动态维护一下即可。
时间复杂度 O(nlogn+nlog|∑|),其中 ∑ 表示字符集。期望得分 84∼100。
#include<iostream>#include<cstdio>#include<cstring>#include<algorithm>
using namespace std;
constint N = 2000000;
constunsignedlonglong P = 13331;
int n;
char S[N];
// suf & prebool exist[30];
int num;
int suf[N];
int pre[N];
int c[N][30];
// hashunsignedlonglong power[N];
unsignedlonglong hash[N];
unsignedlonglongH(int l, int r) {
return hash[r] - hash[l - 1] * power[r - l + 1];
}
voidwork() {
scanf("%s", S + 1);
n = strlen(S + 1);
// suffor (int i = 0; i < 26; i ++)
exist[i] = 0;
num = 0;
for (int i = n; i >= 1; i --) {
int ch = S[i] - 'a';
exist[ch] ^= 1;
if (exist[ch]) num ++;
else num --;
suf[i] = num;
}
// prefor (int i = 0; i < 26; i ++)
exist[i] = 0;
num = 0;
for (int i = 1; i <= n; i ++) {
int ch = S[i] - 'a';
exist[ch] ^= 1;
if (exist[ch]) num ++;
else num --;
pre[i] = num;
}
for (int i = 1; i <= n; i ++) {
for (int j = 0; j <= 26; j ++)
c[i][j] = c[i - 1][j];
for (int j = pre[i]; j <= 26; j ++)
c[i][j] ++;
}
// hashfor (int i = 1; i <= n; i ++)
hash[i] = hash[i - 1] * P + (S[i] - 'a');
// worklonglong ans = 0;
for (int i = 1; i <= n; i ++) {
for (int j = 1; j <= n / i; j ++) {
int l = (j - 1) * i + 1, r = j * i;
if (H(1, i) != H(l, r)) break;
if (r + 1 > n) break;
ans += c[i - 1][suf[r + 1]];
}
}
printf("%lld\n", ans);
}
intmain() {
power[0] = 1;
for (int i = 1; i <= 1500000; i ++) power[i] = power[i - 1] * P;
int T; scanf("%d", &T);
while (T --) work();
return0;
}
// I hope changle_cyx can pray for me.
算法二
「算法一」没有怎么用到题目中的一些性质,还是考虑枚举 T=(AB)=S1..x。
对于当前枚举到的一个 x。考虑计算出一个最大的 i,使得 S 可以被划分成 (AB)iC 的形式,记这个量为 k。
引理:若一个长度为 n 的字符串 S 的前 n−m 位和后 n−m 是相等的且 m∣n,则 S 有一个长度为 m 的整除循环节。
根据该引理,考虑求出字符串 S 的 Z 函数,其中 Zi 表示:后缀 Si..n 与 S 的最长公共前缀(LCP)的长度。
注意到若 S 可以被划分成 (AB)iC 的形式,则必满足 x(i−1)≤Zx+1 且 xi<n。解得
i≤Zx+1x+1i<nx
则
k=min{⌊Zx+1x⌋+1,⌈nx⌉−1}
接下来考虑 F((AB)i) 的重复性,注意到:
当 i 为奇数时 F((AB)i) 均等。当 i 为奇数时,对于每个 (AB)i 划分出来的 C,都有 F(C)=F(Sx+1..n)。
当 i 为偶数时 F((AB)i)=0。当 i 为偶数时,对于每个 (AB)i 划分出来的 C,都有 F(C)=F(S)。
那么我们可以知道,划分出来的 F(C) 的也就只有两种情况,要么是 F(Sx+1..n) 要么是 F(S)。其中 ⌈k2⌉ 个 i 为奇数,⌊k2⌋ 个 i 为偶数。
F(Sx+1..n) 在枚举 x 的时候顺便处理一下即可,F(S) 直接处理即可。
那么现在就是要分别数出 T 中有多少个真前缀 A 满足 F(A)≤F(Sx+1..n),F(A)≤F(S)。
#include<cstdio>#include<cstring>#include<algorithm>
using namespace std;
constint N = (1 << 20) + 1000;
int T;
int n;
char s[N];
int Z[N];
voidZ_algorithm() {
for (int i = 1; i <= n; i ++) Z[i] = 0;
for (int i = 2, l = 0, r = 0; i <= n; i ++) {
if (i <= r) Z[i] = min(Z[i - l + 1], r - i + 1);
while (i + Z[i] <= n && s[1 + Z[i]] == s[i + Z[i]])
Z[i] ++;
if (i + Z[i] - 1 > r)
l = i, r = i + Z[i] - 1;
}
}
int pre, suf, Fs;
int cnt[26];
bool flag1[26], flag2[26];
int cur1, cur2;
longlong ans;
voidwork() {
scanf("%s", s + 1);
n = strlen(s + 1);
Z_algorithm();
memset(flag1, 0, sizeof(flag1));
memset(flag2, 0, sizeof(flag2));
for (int i = 1; i <= n; i ++) {
int ch = s[i] - 'a';
flag2[ch] ^= 1;
}
Fs = 0;
for (int i = 0; i < 26; i ++)
if (flag2[i]) Fs ++;
pre = 0, suf = Fs;
ans = 0, cur1 = cur2 = 0;
memset(cnt, 0, sizeof(cnt));
for (int i = 1; i < n; i ++) {
int ch = s[i] - 'a';
flag1[ch] ^= 1, flag2[ch] ^= 1;
if (flag1[ch]) pre ++;
else pre --;
if (flag2[ch]) suf ++, cur1 += cnt[suf];
else cur1 -= cnt[suf], suf --;
int k = min((Z[i + 1] / i) + 1, (n - 1) / i);
int odd = (k + 1) / 2, even = k / 2;
ans += 1ll * odd * cur1;
ans += 1ll * even * cur2;
cnt[pre] ++;
if (pre <= suf) cur1 ++;
if (pre <= Fs) cur2 ++;
}
printf("%lld\n", ans);
}
intmain() {
scanf("%d", &T);
while (T --) work();
return0;
}
// I hope changle_cyx can pray for me.
#include<cstdio>#include<cstring>#include<algorithm>
using namespace std;
inlineintread() {
int x = 0, f = 1; char s = getchar();
while (s < '0' || s > '9') { if (s == '-') f = -f; s = getchar(); }
while (s >= '0' && s <= '9') { x = x * 10 + s - '0'; s = getchar(); }
return x * f;
}
constint N = 60, M = 450;
int n, m;
int top[N], a[N][M];
int cnt[N][M];
int t;
pair<int, int> ans[820001];
voidmove(int x, int y) {
ans[++ t] = make_pair(x, y);
int u = a[x][top[x]];
cnt[x][u] --;
cnt[y][u] ++;
a[y][++ top[y]] = a[x][top[x] --];
}
voidsolve(int u) {
if (u == 1)
return;
for (int i = 1; i < u; i ++) {
int c = cnt[i][u];
for (int j = c; j; j --)
move(u, u + 1);
for (int j = m; j; j --)
if (a[i][j] == u) move(i, u);
else move(i, u + 1);
for (int j = m - c; j; j --)
move(u + 1, i);
for (int j = c; j; j --)
move(u, i);
for (int j = c; j; j --)
move(u + 1, u);
}
for (int i = 1; i < u; i ++)
while (a[i][top[i]] == u)
move(i, u + 1);
int p = 1;
for (int j = top[u]; j; j --) {
if (a[u][j] == u) move(u, u + 1);
else {
while (top[p] >= m) p ++;
move(u, p);
}
}
solve(u - 1);
}
intmain() {
n = read(), m = read();
for (int i = 1; i <= n; i ++) {
top[i] = m;
for (int j = 1; j <= m; j ++)
a[i][j] = read(), cnt[i][a[i][j]] ++;
}
solve(n);
printf("%d\n", t);
for (int i = 1; i <= t; i ++)
printf("%d %d\n", ans[i].first, ans[i].second);
return0;
}
// I hope changle_cyx can pray for me.
#include<cstdio>#include<cstring>#include<algorithm>#include<vector>
using namespace std;
inlineintread() {
int x = 0, f = 1; char s = getchar();
while (s < '0' || s > '9') { if (s == '-') f = -f; s = getchar(); }
while (s >= '0' && s <= '9') { x = x * 10 + s - '0'; s = getchar(); }
return x * f;
}
constint N = 60, M = 410;
int n, m;
int top[N], a[N][M];
int em;
int t;
pair<int, int> ans[820001];
voidmove(int x, int y) {
ans[++ t] = make_pair(x, y);
a[y][++ top[y]] = a[x][top[x] --];
}
bool impx[M], impy[M];
intmerge(int x, int y, int mid) {
int cx = 0, cy = 0;
for (int i = 1; i <= m; i ++)
impx[i] = a[x][i] <= mid,
impy[i] = a[y][i] <= mid;
for (int i = 1; i <= m; i ++)
cx += impx[i], cy += impy[i];
if (cx + cy > m) {
cx = m - cx, cy = m - cy;
for (int i = 1; i <= m; i ++)
impx[i] ^= 1, impy[i] ^= 1;
}
for (int i = 1; i <= m; i ++)
if (!impx[i] && cx + cy < m)
impx[i] = 1, cx ++;
for (int i = cx; i; i --)
move(y, em);
for (int i = m; i; i --)
if (impx[i]) move(x, y);
else move(x, em);
for (int i = m - cx; i; i --)
move(em, x);
for (int i = cx; i; i --)
move(y, x);
for (int i = cx; i; i --)
move(em, y);
for (int i = cx; i; i --)
move(x, em);
for (int i = m; i; i --)
if (impy[i]) move(y, em);
else move(y, x);
int p = em; em = y;
return p;
}
voidsolve(int l, int r) {
if (l == r) return;
int mid = (l + r) >> 1;
vector<int> now;
for (int i = 1; i <= n + 1; i ++) {
if (i == em) continue;
if (l <= a[i][1] && a[i][1] <= r) now.push_back(i);
}
for (int i = 0; i + 1 < (int)now.size(); i ++)
now[i + 1] = merge(now[i], now[i + 1], mid);
solve(l, mid), solve(mid + 1, r);
}
intmain() {
n = read(), m = read();
em = n + 1;
for (int i = 1; i <= n; i ++) {
top[i] = m;
for (int j = 1; j <= m; j ++)
a[i][j] = read();
}
solve(1, n);
printf("%d\n", t);
for (int i = 1; i <= t; i ++)
printf("%d %d\n", ans[i].first, ans[i].second);
return0;
}
// I hope changle_cyx can pray for me.
#include<cstdio>#include<cstring>#include<algorithm>
using namespace std;
inlineintread() {
int x = 0, f = 1; char s = getchar();
while (s < '0' || s > '9') { if (s == '-') f = -f; s = getchar(); }
while (s >= '0' && s <= '9') { x = x * 10 + s - '0'; s = getchar(); }
return x * f;
}
constint N = 500100, SIZE = 11;
constint mod = 1e9 + 7;
int n, k;
int w[SIZE];
int c[N], d[N];
int L[SIZE], R[SIZE];
int v[SIZE];
int u[SIZE];
int cur[SIZE];
int ans;
intmain() {
n = read(), k = read();
for (int i = 1; i <= k; i ++)
w[i] = read();
for (int i = 1; i <= n; i ++)
c[i] = read(), d[i] = read();
// judgment has no answer.for (int i = 1; i <= k; i ++)
L[i] = 1, R[i] = w[i];
for (int i = 1; i <= n; i ++) {
v[c[i]] += d[i];
if (v[c[i]]) {
if (v[c[i]] > 0) {
int val = v[c[i]];
if (w[c[i]] - val < R[c[i]]) R[c[i]] = w[c[i]] - val;
} else {
int val = -v[c[i]];
if (val + 1 > L[c[i]]) L[c[i]] = val + 1;
}
}
}
bool flag1 = 1;
for (int i = 1; i <= k; i ++)
if (L[i] > R[i]) flag1 = 0;
bool flag2 = 1;
for (int i = 1; i <= k; i ++)
if (v[i]) flag2 = 0;
if (flag1 && flag2) {
puts("-1");
return0;
}
// workfor (int i = 1; i <= k; i ++)
L[i] = 1, R[i] = w[i];
for (int i = 1; i <= n; i ++) {
int C = c[i], D = d[i];
u[C] += D;
bool great = 0;
if (u[C]) {
if (u[C] > 0) {
int val = u[C];
if (w[C] - val < R[C])
R[C] = w[C] - val, great = 1;
} else {
int val = -u[C];
if (val + 1 > L[C])
L[C] = val + 1, great = 1;
}
}
if (great) {
int val = 1;
for (int j = 1; j <= k; j ++) {
if (j == C) continue;
val = 1ll * val * (R[j] - L[j] + 1) % mod;
}
ans = (ans + 1ll * val * i) % mod;
if (L[C] > R[C]) {
printf("%d\n", ans);
return0;
}
}
}
for (int i = n + 1; i <= 2 * n; i ++) {
int C = c[i - n], D = d[i - n];
u[C] += D;
bool great = 0;
if (u[C]) {
if (u[C] > 0) {
int val = u[C];
if (w[C] - val < R[C])
great = 1;
} else {
int val = -u[C];
if (val + 1 > L[C])
great = 1;
}
}
if (great) {
int mul = i;
for (int j = 1; j <= k; j ++) cur[j] = R[j] - L[j] + 1;
bool keep = 1;
while (keep) {
int val = 1;
for (int j = 1; j <= k; j ++) {
if (j == C) continue;
val = 1ll * val * cur[j] % mod;
}
ans = (ans + 1ll * val * mul) % mod;
mul = (mul + n) % mod;
for (int j = 1; j <= k; j ++) {
cur[j] -= abs(v[j]);
if (cur[j] <= 0) {
keep = 0;
break;
}
}
}
}
if (u[C]) {
if (u[C] > 0) {
int val = u[C];
if (w[C] - val < R[C])
R[C] = w[C] - val;
} else {
int val = -u[C];
if (val + 1 > L[C])
L[C] = val + 1;
}
}
if (L[C] > R[C]) {
printf("%d\n", ans);
return0;
}
}
printf("%d\n", ans);
return0;
}
// I hope changle_cyx can pray for me.
算法二
特殊性质:wi≤109。
看起来「算法一」非常有前途,考虑优化。优化的重点其实在于计算模 n 的所有同余类 ¯¯¯1,¯¯¯2,⋯,¯¯¯¯¯¯¯¯¯¯¯¯¯n−1,¯¯¯0 对答案的贡献。
对于第二周期中的第 i 步(其中 n<i≤2n),若第 i 步是一个 " 特殊步 "。
考虑第 n∗x+(i−n) 步,若该步也是一个 " 特殊步 ",则不难发现,该步对答案的贡献是一个与 x 有关的多项式,可以考虑用 f(x) 表示:
f(x)=[n×x+(i−n)]∏j≠ci[−|vj|×x+(Rj−Lj+1+|vj|)]
注意到 f(x) 的每个乘积项,都是关于 x 的一次二项式。
可以考虑将这 k 个乘积项卷在一起,即可得到一个 k 次多项式 f(x)。至于计算出 f(x) 的系数,直接暴力卷即可,因为 k 很小,所以复杂度肯定是可以接受的。
代码中并没有直接拉格朗日插值,而是使用拉格朗日插值先预处理出 Sk(n) 所对应的关于 n 的 k+1 次多项式,然后再代入 x 进行计算。
时间复杂度 O(nk2)。
#include<cstdio>#include<cstring>#include<algorithm>
using namespace std;
inlineintread() {
int x = 0, f = 1; char s = getchar();
while (s < '0' || s > '9') { if (s == '-') f = -f; s = getchar(); }
while (s >= '0' && s <= '9') { x = x * 10 + s - '0'; s = getchar(); }
return x * f;
}
intpower(int a, int b, int p) {
int ans = 1;
for (; b; b >>= 1) {
if (b & 1) ans = 1ll * ans * a % p;
a = 1ll * a * a % p;
}
return ans;
}
constint N = 500100, SIZE = 20;
constint mod = 1e9 + 7;
int n, k;
int w[SIZE];
int c[N], d[N];
int L[SIZE], R[SIZE];
int v[SIZE];
int u[SIZE];
structpolynomial {int m;
int num[SIZE];
polynomial() {
m = 0;
memset(num, 0, sizeof(num));
}
polynomial(int a, int b, int c) { m = a, num[0] = b, num[1] = c; }
};
polynomial operator + (polynomial a, polynomial b) {
polynomial c;
c.m = a.m;
for (int i = 0; i <= a.m; i ++)
c.num[i] = (a.num[i] + b.num[i]) % mod;
return c;
}
polynomial operator * (polynomial a, polynomial b) {
polynomial c;
c.m = a.m + b.m;
for (int i = 0; i <= a.m; i ++)
for (int j = 0; j <= b.m; j ++)
c.num[i + j] = ((c.num[i + j] + 1ll * a.num[i] * b.num[j]) % mod + mod) % mod;
return c;
}
intcalc(polynomial a, int x) {
int ans = 0;
for (int i = 0, val = 1; i <= a.m; i ++, val = 1ll * val * x % mod)
ans = (ans + 1ll * a.num[i] * val) % mod;
return ans;
}
polynomial g[SIZE];
int val[SIZE];
voidLagrange(int K) {
val[0] = 0;
for (int i = 1; i <= K + 2; i ++) val[i] = (val[i - 1] + power(i, K, mod)) % mod;
g[K].m = K + 1;
for (int i = 1; i <= K + 2; i ++) {
polynomial p = polynomial(0, 1, 0);
int q = 1;
for (int j = 1; j <= K + 2; j ++) {
if (i == j) continue;
p = p * polynomial(1, mod - j, 1);
q = 1ll * q * (((i - j) + mod) % mod) % mod;
}
p = p * polynomial(0, power(q, mod - 2, mod), 0);
p = p * polynomial(0, val[i], 0);
g[K] = g[K] + p;
}
}
int ans;
intmain() {
n = read(), k = read();
for (int i = 1; i <= k; i ++)
w[i] = read();
for (int i = 1; i <= n; i ++)
c[i] = read(), d[i] = read();
// judgment has no answer.for (int i = 1; i <= k; i ++)
L[i] = 1, R[i] = w[i];
for (int i = 1; i <= n; i ++) {
v[c[i]] += d[i];
if (v[c[i]]) {
if (v[c[i]] > 0) {
int val = v[c[i]];
if (w[c[i]] - val < R[c[i]]) R[c[i]] = w[c[i]] - val;
} else {
int val = -v[c[i]];
if (val + 1 > L[c[i]]) L[c[i]] = val + 1;
}
}
}
bool flag1 = 1;
for (int i = 1; i <= k; i ++)
if (L[i] > R[i]) flag1 = 0;
bool flag2 = 1;
for (int i = 1; i <= k; i ++)
if (v[i]) flag2 = 0;
if (flag1 && flag2) {
puts("-1");
return0;
}
// workfor (int i = 1; i <= k; i ++)
L[i] = 1, R[i] = w[i];
for (int i = 1; i <= n; i ++) {
int C = c[i], D = d[i];
u[C] += D;
bool great = 0;
if (u[C]) {
if (u[C] > 0) {
int val = u[C];
if (w[C] - val < R[C])
R[C] = w[C] - val, great = 1;
} else {
int val = -u[C];
if (val + 1 > L[C])
L[C] = val + 1, great = 1;
}
}
if (great) {
int val = 1;
for (int j = 1; j <= k; j ++) {
if (j == C) continue;
val = 1ll * val * (R[j] - L[j] + 1) % mod;
}
ans = (ans + 1ll * val * i) % mod;
if (L[C] > R[C]) {
printf("%d\n", ans);
return0;
}
}
}
for (int i = 0; i <= k; i ++)
Lagrange(i);
for (int i = n + 1; i <= 2 * n; i ++) {
int C = c[i - n], D = d[i - n];
u[C] += D;
bool great = 0;
if (u[C]) {
if (u[C] > 0) {
int val = u[C];
if (w[C] - val < R[C])
great = 1;
} else {
int val = -u[C];
if (val + 1 > L[C])
great = 1;
}
}
if (great) {
int Round = 0x3f3f3f3f;
for (int j = 1; j <= k; j ++) {
if (!v[j]) continue;
Round = min(Round, ((R[j] - L[j]) / abs(v[j])) + 1);
}
polynomial f = polynomial(0, 1, 0);
f = f * polynomial(1, i - n, n);
for (int j = 1; j <= k; j ++) {
if (j == C) continue;
f = f * polynomial(1, R[j] - L[j] + 1 + abs(v[j]), mod - abs(v[j]));
}
for (int j = 0; j <= k; j ++)
ans = (ans + 1ll * f.num[j] * calc(g[j], Round)) % mod;
}
if (u[C]) {
if (u[C] > 0) {
int val = u[C];
if (w[C] - val < R[C])
R[C] = w[C] - val;
} else {
int val = -u[C];
if (val + 1 > L[C])
L[C] = val + 1;
}
}
if (L[C] > R[C]) {
printf("%d\n", ans);
return0;
}
}
printf("%d\n", ans);
return0;
}
// I hope changle_cyx can pray for me.
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 从 HTTP 原因短语缺失研究 HTTP/2 和 HTTP/3 的设计差异
· AI与.NET技术实操系列:向量存储与相似性搜索在 .NET 中的实现
· 基于Microsoft.Extensions.AI核心库实现RAG应用
· Linux系列:如何用heaptrack跟踪.NET程序的非托管内存泄露
· 开发者必知的日志记录最佳实践
· winform 绘制太阳,地球,月球 运作规律
· TypeScript + Deepseek 打造卜卦网站:技术与玄学的结合
· AI 智能体引爆开源社区「GitHub 热点速览」
· 写一个简单的SQL生成工具
· Manus的开源复刻OpenManus初探