c 0 = a 1 b 2 + a 2 b 1 + a 1 b 1 + a 2 b 2 c_0 = a_1b_2 + a_2b_1 + a_1b_1 + a_2b_2 c0=a1b2+a2b1+a1b1+a2b2
c 1 = a 0 b 2 + a 2 b 0 + a 0 b 0 c_1 = a_0b_2 + a_2b_0 + a_0b_0 c1=a0b2+a2b0+a0b0
c 2 = a 0 b 1 + a 1 b 0 c_2 = a_0b_1 + a_1b_0 c2=a0b1+a1b0
计算 ( a 1 + a 2 ) ( b 1 + b 2 ) , ( a 0 + a 2 ) ( b 0 + b 2 ) , a 2 b 2 , ( a 0 + a 1 + a 2 ) ( b 0 + b 1 + b 2 ) (a_1+a_2)(b_1+b_2), (a_0+a_2)(b_0+b_2), a_2b_2, (a_0+a_1+a_2)(b_0+b_1+b_2) (a1+a2)(b1+b2),(a0+a2)(b0+b2),a2b2,(a0+a1+a2)(b0+b1+b2) ,线性组合求出 c c c ,递归计算即可。
#include
using namespace std;
vector<long long> multiply(const vector<int> &a, const vector<int> &b, int n) {
if (n == 1) {
return vector<long long>(1, (long long) a[0] * b[0]);
} else {
n /= 3;
vector<int> aa(n), bb(n);
for (int i = 0; i < n; ++i) {
aa[i] = a[i + n * 2];
bb[i] = b[i + n * 2];
}
vector<long long> foo = multiply(aa, bb, n);
for (int i = 0; i < n; ++i) {
aa[i] += a[i];
bb[i] += b[i];
}
vector<long long> bar = multiply(aa, bb, n);
for (int i = 0; i < n; ++i) {
aa[i] += a[i + n];
bb[i] += b[i + n];
}
vector<long long> baz = multiply(aa, bb, n);
for (int i = 0; i < n; ++i) {
aa[i] -= a[i];
bb[i] -= b[i];
}
vector<long long> c = multiply(aa, bb, n);
c.resize(n * 3);
for (int i = 0; i < n; ++i) {
c[i + n] = bar[i] - foo[i];
c[i + n * 2] = baz[i] - c[i] - c[i + n];
}
return c;
}
}
int main() {
#ifdef wxh010910
freopen("input.txt", "r", stdin);
#endif
ios::sync_with_stdio(false);
cin.tie(0);
cout.tie(0);
int n;
cin >> n;
int m = 1;
for (int i = 0; i < n; ++i) {
m *= 3;
}
vector<int> a(m, 1), b(m, 1);
for (int i = 0; i < m; ++i) {
cin >> a[i];
}
for (int i = 0; i < m; ++i) {
cin >> b[i];
}
vector<long long> c = multiply(a, b, m);
for (int i = 0; i < m; ++i) {
if (i) {
cout << " ";
}
cout << c[i];
}
cout << "\n";
return 0;
}
可以发现最后只需要满足某种性质的矩阵即可表示出所有答案,具体可以参见代码。(我咋知道怎么发现的)
#include
using namespace std;
int main() {
#ifdef wxh010910
freopen("input.txt", "r", stdin);
#endif
ios::sync_with_stdio(false);
cin.tie(0);
cout.tie(0);
int n;
cin >> n;
vector<vector<int>> same(19683, vector<int>(27, -1));
vector<vector<int>> small(3, vector<int>(3));
vector<vector<int>> with(3, vector<int>(3));
vector<vector<int>> without(3, vector<int>(3));
vector<int> to(3);
auto get = [&](int hash_all, int hash_to) {
if (same[hash_all][hash_to] == -1) {
int res = 0;
for (int i = 0; i < 3; ++i) {
for (int j = 0; j < 3; ++j) {
if (to[small[i][j]] == small[i][to[j]]) {
++res;
}
}
}
same[hash_all][hash_to] = res;
}
return same[hash_all][hash_to];
};
auto solve = [&](int a, int b, int d, int e, int g, int h) {
for (int i = 0; i < 3; ++i) {
small[0][i] = i < a ? 0 : i < b ? 1 : 2;
small[1][i] = i < d ? 0 : i < e ? 2 : 0;
small[2][i] = i < g ? 0 : i < h ? 2 : 1;
}
with[0][0] = a;
with[0][1] = b - a;
with[0][2] = n - b;
with[1][0] = d + n - e;
with[1][1] = 0;
with[1][2] = e - d;
with[2][0] = g;
with[2][1] = n - h;
with[2][2] = h - g;
without = with;
for (int i = 0; i < 3; ++i) {
for (int j = 0; j < 3; ++j) {
--without[i][small[i][j]];
}
}
int res = 0;
res += with[0][0] * n * (n - 3);
for (int i = 0; i < 3; ++i) {
for (int j = 0; j < 3; ++j) {
res += without[i][j] * with[j][small[i][0]];
}
}
int hash_all = 0;
for (int i = 0; i < 3; ++i) {
for (int j = 0; j < 3; ++j) {
hash_all = hash_all * 3 + small[i][j];
}
}
vector<int> disc = {0, a, b, d, e, g, h, n};
sort(disc.begin(), disc.end());
disc.erase(unique(disc.begin(), disc.end()), disc.end());
for (int i = 0; i + 1 < (int) disc.size(); ++i) {
int p = disc[i];
to[0] = p < a ? 0 : p < b ? 1 : 2;
to[1] = p < d ? 0 : p < e ? 2 : 0;
to[2] = p < g ? 0 : p < h ? 2 : 1;
res += (disc[i + 1] - disc[i]) * get(hash_all, to[0] * 9 + to[1] * 3 + to[2]);
}
return res;
};
vector<vector<int>> ans(n * n * n + 1);
if (n >= 3) {
int need = n * n * n + 1;
for (int a = 0; a <= n; ++a) {
for (int b = a; b <= n && min(a, b - a) == 0; ++b) {
for (int d = 0; d <= 2 - (n >= 20); ++d) {
for (int e = d; e <= n && min(d, e - d) <= 1; ++e) {
for (int g = 0; g <= n; ++g) {
for (int h = g; h <= n; ++h) {
int c = solve(a, b, d, e, g, h);
if (ans[c].empty()) {
ans[c] = vector<int>{a, b, d, e, g, h};
--need;
if (!need) {
goto found;
}
}
}
}
}
}
}
}
found:;
}
int q;
cin >> q;
while (q--) {
int need;
cin >> need;
if (n == 1) {
if (need == 1) {
cout << "YES" << "\n";
cout << 1 << "\n";
} else {
cout << "NO" << "\n";
}
} else if (n == 2) {
vector<vector<int>> res(n, vector<int>(n));
for (int a = 0; a < n; ++a) {
for (int b = 0; b < n; ++b) {
for (int c = 0; c < n; ++c) {
for (int d = 0; d < n; ++d) {
res[0][0] = a;
res[0][1] = b;
res[1][0] = c;
res[1][1] = d;
int cnt = 0;
for (int i = 0; i < n; ++i) {
for (int j = 0; j < n; ++j) {
for (int k = 0; k < n; ++k) {
if (res[res[i][j]][k] == res[i][res[j][k]]) {
++cnt;
}
}
}
}
if (cnt == need) {
cout << "YES" << "\n";
cout << a + 1 << " " << b + 1 << "\n";
cout << c + 1 << " " << d + 1 << "\n";
goto outer;
}
}
}
}
}
cout << "NO" << "\n";
outer:;
} else {
vector<vector<int>> res(n, vector<int>(n));
for (int i = 0; i < n; ++i) {
res[0][i] = i < ans[need][0] ? 0 : i < ans[need][1] ? 1 : 2;
res[1][i] = i < ans[need][2] ? 0 : i < ans[need][3] ? 2 : 0;
res[2][i] = i < ans[need][4] ? 0 : i < ans[need][5] ? 2 : 1;
}
cout << "YES" << "\n";
for (int i = 0; i < n; ++i) {
for (int j = 0; j < n; ++j) {
if (j) {
cout << " ";
}
cout << res[i][j] + 1;
}
cout << "\n";
}
}
}
return 0;
}
题意即为 − ( x + 1 ) a ( x − 1 ) n − a -(x+1)^a(x-1)^{n-a} −(x+1)a(x−1)n−a ,求 [ x k ] [x^k] [xk] 系数可以把它表示成一个关于 a a a 的多项式,注意到 x = a x=a x=a 时, P ( a ) = ( P ( x )   m o d   ( x − a ) ) ( a ) P(a)=(P(x)\bmod (x-a))(a) P(a)=(P(x)mod(x−a))(a) ,那么我们求出一堆 P ( x ) − P ( a ) P(x)-P(a) P(x)−P(a) 的多项式 gcd \gcd gcd 即可解出 a a a 。
#include
using namespace std;
const int md = (int) 2e7 - 1;
inline void add(int &x, int y) {
x += y;
if (x >= md) {
x -= md;
}
}
inline void sub(int &x, int y) {
x -= y;
if (x < 0) {
x += md;
}
}
inline int mul(int x, int y) {
return (int) ((long long) x * y % md);
}
inline int inv(int a) {
int b = md, u = 0, v = 1;
while (a) {
int t = b / a;
b -= t * a;
swap(a, b);
u -= t * v;
swap(u, v);
}
if (u < 0) {
u += md;
}
return u;
}
vector<int>& operator += (vector<int> &a, const vector<int> &b) {
if (a.size() < b.size()) {
a.resize(b.size());
}
for (int i = 0; i < (int) b.size(); ++i) {
add(a[i], b[i]);
}
return a;
}
vector<int> operator + (const vector<int> &a, const vector<int> &b) {
vector<int> c = a;
return c += b;
}
vector<int>& operator -= (vector<int> &a, const vector<int> &b) {
if (a.size() < b.size()) {
a.resize(b.size());
}
for (int i = 0; i < (int) b.size(); ++i) {
sub(a[i], b[i]);
}
return a;
}
vector<int> operator - (const vector<int> &a, const vector<int> &b) {
vector<int> c = a;
return c -= b;
}
vector<int>& operator *= (vector<int> &a, const vector<int> &b) {
vector<int> c = a;
a.assign(a.size() + b.size() - 1, 0);
for (int i = 0; i < (int) c.size(); ++i) {
for (int j = 0; j < (int) b.size(); ++j) {
add(a[i + j], mul(c[i], b[j]));
}
}
return a;
}
vector<int> gcd(vector<int> a, vector<int> b) {
while (true) {
while (!a.empty() && a.back() == 0) {
a.pop_back();
}
while (!b.empty() && b.back() == 0) {
b.pop_back();
}
if (a.size() > b.size()) {
swap(a, b);
}
if (a.empty()) {
return b;
}
int coef = mul(b.back(), inv(a.back()));
for (int i = 0; i < (int) a.size(); ++i) {
sub(b[b.size() - a.size() + i], mul(a[i], coef));
}
}
}
int main() {
int n;
cin >> n;
--n;
vector<int> fact(n + 1), inv_fact(n + 1);
fact[0] = fact[1] = inv_fact[0] = inv_fact[1] = 1;
for (int i = 2; i <= n; ++i) {
fact[i] = mul(fact[i - 1], i);
inv_fact[i] = mul(inv_fact[md % i], md - md / i);
}
for (int i = 2; i <= n; ++i) {
inv_fact[i] = mul(inv_fact[i - 1], inv_fact[i]);
}
auto binom = [&](int x, int y) {
return x < y ? 0 : mul(fact[x], mul(inv_fact[y], inv_fact[x - y]));
};
auto make_poly = [&](int d) {
vector<int> res;
for (int i = 0; i <= d; ++i) {
vector<int> poly(1, 1);
for (int j = 0; j < i; ++j) {
poly *= vector<int>{md - j, 1};
}
for (int j = 0; j < d - i; ++j) {
poly *= vector<int>{n - j, md - 1};
}
poly *= vector<int>(1, mul(inv_fact[i], inv_fact[d - i]));
if ((n + d + i) & 1) {
res += poly;
} else {
res -= poly;
}
}
return res;
};
vector<int> pos, neg;
int a = -1;
int d = 128;
while (true) {
vector<int> poly = make_poly(d);
cout << "? " << d + 1 << endl;
int res;
cin >> res;
if (res < 0) {
res += md;
}
pos = gcd(pos, poly - vector<int>(1, res));
neg = gcd(neg, poly + vector<int>(1, res));
vector<int> candidates;
auto check = [&](vector<int> v, int parity) {
if ((int) v.size() != 2) {
return;
}
int a = (md - mul(v[0], inv(v[1]))) % md;
if ((a & 1) == parity && a <= n) {
candidates.push_back(a);
}
};
check(pos, 0);
check(neg, 1);
if ((int) candidates.size() == 1) {
a = candidates[0];
break;
}
++d;
}
cout << "!";
for (int d = 0; d < 128; ++d) {
int res = 0;
for (int i = 0; i <= d; ++i) {
int coef = mul(binom(a, i), binom(n - a, d - i));
if ((n + d + i + a) & 1) {
add(res, coef);
} else {
sub(res, coef);
}
}
if (res >= (int) 1e7) {
res -= md;
}
cout << " " << res;
}
cout << endl;
return 0;
}
线性递推求出对应系数后高斯消元即可。
#include
using namespace std;
const int md = (int) 1e9 + 7;
inline void add(int &x, int y) {
x += y;
if (x >= md) {
x -= md;
}
}
inline void sub(int &x, int y) {
x -= y;
if (x < 0) {
x += md;
}
}
inline int mul(int x, int y) {
return (int) ((long long) x * y % md);
}
inline int inv(int a) {
int b = md, u = 0, v = 1;
while (a) {
int t = b / a;
b -= t * a;
swap(a, b);
u -= t * v;
swap(u, v);
}
if (u < 0) {
u += md;
}
return u;
}
vector<int>& operator *= (vector<int> &a, vector<int> b) {
vector<int> c = a;
a.assign(a.size() + b.size() - 1, 0);
for (int i = 0; i < (int) c.size(); ++i) {
for (int j = 0; j < (int) b.size(); ++j) {
add(a[i + j], mul(c[i], b[j]));
}
}
return a;
}
vector<int>& operator %= (vector<int> &a, const vector<int> &b) {
while (a.size() >= b.size()) {
int coef = mul(a.back(), inv(b.back()));
for (int i = 0; i < (int) b.size(); ++i) {
sub(a[a.size() - b.size() + i], mul(b[i], coef));
}
a.pop_back();
}
return a;
}
int main() {
#ifdef wxh010910
freopen("input.txt", "r", stdin);
#endif
ios::sync_with_stdio(false);
cin.tie(0);
cout.tie(0);
int n;
cin >> n;
vector<int> a(n + 1);
a[n] = 1;
for (int i = n - 1; ~i; --i) {
cin >> a[i];
a[i] = (md - a[i]) % md;
}
vector<int> b(n);
for (int i = 0; i < n; ++i) {
cin >> b[i];
}
vector<vector<int>> g(n, vector<int>(n + 1));
auto add = [&](int foo, int bar) {
vector<int> res = vector<int>{1};
vector<int> x = vector<int>{0, 1};
x %= a;
while (foo) {
if (foo & 1) {
res *= x;
res %= a;
}
x *= x;
x %= a;
foo >>= 1;
}
res.resize(n);
for (int i = 0; i < n; ++i) {
g[i][bar] = res[i];
}
};
for (int i = 0; i < n; ++i) {
add(b[n - 1] - b[i], i);
}
add(b[n - 1], n);
for (int i = 0; i < n; ++i) {
int p = i;
while (!g[p][i]) {
++p;
}
if (p != i) {
swap(g[i], g[p]);
}
int coef = inv(g[i][i]);
for (int j = i; j <= n; ++j) {
g[i][j] = mul(g[i][j], coef);
}
for (int j = 0; j < n; ++j) {
if (j != i) {
int coef = g[j][i];
for (int k = i; k <= n; ++k) {
sub(g[j][k], mul(g[i][k], coef));
}
}
}
}
for (int i = 0; i < n; ++i) {
if (i) {
cout << " ";
}
cout << g[i][n];
}
cout << "\n";
return 0;
}
答案是 ⌈ s u m 2 ⌉ \lceil \frac{sum}{2}\rceil ⌈2sum⌉ 。
#include
using namespace std;
int main() {
#ifdef wxh010910
freopen("input.txt", "r", stdin);
#endif
ios::sync_with_stdio(false);
cin.tie(0);
cout.tie(0);
int n;
cin >> n;
int ans = 0;
while (n--) {
int x;
cin >> x;
ans += x;
}
cout << (ans + 1) / 2 << "\n";
return 0;
}
考虑从大到小加边,维护所有边和主要边的并查集,如果两个点在所有边的并查集中连通,在树边中不连通,则它们均不合法。启发式合并所有合法点即可。
#include
using namespace std;
class dsu {
public:
vector<vector<int>> good;
vector<int> sz;
vector<int> p;
int n;
dsu(int n, bool add): n(n) {
p.resize(n);
sz.resize(n);
good.resize(n);
for (int i = 0; i < n; ++i) {
p[i] = i;
sz[i] = 1;
if (add) {
good[i].push_back(i);
}
}
}
int find(int x) {
while (x != p[x]) {
x = p[x] = p[p[x]];
}
return x;
}
bool unite(int x, int y) {
x = find(x);
y = find(y);
if (x != y) {
if (good[x].size() > good[y].size()) {
swap(x, y);
}
p[x] = y;
sz[y] += sz[x];
for (auto z : good[x]) {
good[y].push_back(z);
}
return true;
} else {
return false;
}
}
};
int main() {
#ifdef wxh010910
freopen("input.txt", "r", stdin);
#endif
ios::sync_with_stdio(false);
cin.tie(0);
cout.tie(0);
int n, m;
cin >> n >> m;
vector<int> type(m), from(m), to(m), cost(m), order(m);
for (int i = 0; i < m; ++i) {
cin >> type[i] >> from[i] >> to[i] >> cost[i];
--from[i];
--to[i];
order[i] = i;
}
sort(order.begin(), order.end(), [&](int x, int y) {
return cost[x] > cost[y];
});
dsu all(n, true), major(n, false);
for (int i = 0, j = 0; i < m; i = j) {
while (j < m && cost[order[j]] == cost[order[i]]) {
++j;
}
for (int k = i; k < j; ++k) {
all.unite(from[order[k]], to[order[k]]);
if (type[order[k]]) {
major.unite(from[order[k]], to[order[k]]);
}
}
for (int k = i; k < j; ++k) {
if (all.sz[all.find(from[order[k]])] != major.sz[major.find(from[order[k]])]) {
all.good[all.find(from[order[k]])].clear();
}
}
}
vector<int> ans = all.good[all.find(0)];
sort(ans.begin(), ans.end());
cout << ans.size() << "\n";
for (int i = 0; i < (int) ans.size(); ++i) {
if (i) {
cout << " ";
}
cout << ans[i] + 1;
}
cout << "\n";
return 0;
}
当 p p p 的环多于两个时,答案为 0 0 0 。两个环例如环长是 a , b a, b a,b ,则用前 a a a 行减去后 a a a 行得到 0 0 0 向量,即矩阵不满秩;更多的环归纳即可。
剩下我们需要求 cyclic matrix
的行列式,可以证明其行列式为 ∏ i = 0 n A ( ω n i ) \prod_{i=0}^n A(\omega_n^i) ∏i=0nA(ωni) ,注意到 ω n i \omega_n^i ωni 是 B ( x ) = x n − 1 B(x)=x^n-1 B(x)=xn−1 的所有根,则我们要求的就是 A A A 和 B B B 的 resultant
,具体细节可以看官方题解或者wiki。
#include
using namespace std;
const int md = (int) 1e9 + 7;
inline void sub(int &x, int y) {
x -= y;
if (x < 0) {
x += md;
}
}
inline int mul(int x, int y) {
return (int) ((long long) x * y % md);
}
inline int inv(int a) {
int b = md, u = 0, v = 1;
while (a) {
int t = b / a;
b -= t * a;
swap(a, b);
u -= t * v;
swap(u, v);
}
if (u < 0) {
u += md;
}
return u;
}
int resultant(vector<int> a, vector<int> b) {
int ans = 1;
while (true) {
int n = a.size() - 1;
int m = b.size() - 1;
if (n > m) {
swap(n, m);
swap(a, b);
if ((n & 1) && (m & 1)) {
ans = (md - ans) % md;
}
}
if (!n) {
for (int i = 0; i < m; ++i) {
ans = mul(ans, a[0]);
}
return ans;
}
int coef = mul(b.back(), inv(a.back()));
for (int i = 0; i < (int) a.size(); ++i) {
sub(b[b.size() - a.size() + i], mul(a[i], coef));
}
while ((int) b.size() > 1 && b.back() == 0) {
ans = mul(ans, a[n]);
b.pop_back();
}
}
}
int main() {
#ifdef wxh010910
freopen("input.txt", "r", stdin);
#endif
ios::sync_with_stdio(false);
cin.tie(0);
cout.tie(0);
int n;
cin >> n;
vector<int> a(n);
for (int i = 0; i < n; ++i) {
cin >> a[i];
}
vector<int> p(n);
for (int i = 0; i < n; ++i) {
cin >> p[i];
--p[i];
}
vector<int> new_a;
for (int i = 0; new_a.empty() || i; i = p[i]) {
new_a.push_back(a[i]);
}
swap(a, new_a);
if ((int) a.size() != n) {
cout << 0 << "\n";
return 0;
}
vector<int> c(n);
for (int i = 0, j = 0; i < n; ++i, j = p[j]) {
c[j] = (n - i) % n;
}
int ans = 1;
for (int i = 0; i < n; ++i) {
for (int j = 0; j < i; ++j) {
if (c[j] > c[i]) {
ans = md - ans;
}
}
}
vector<int> b(n + 1);
b[n] = 1;
b[0] = md - 1;
cout << mul(ans, resultant(a, b)) << "\n";
return 0;
}
二分出平衡点的整数部分,小数部分可以直接算。
#include
using namespace std;
const int md = (int) 1e9 + 7;
inline void sub(int &x, int y) {
x -= y;
if (x < 0) {
x += md;
}
}
inline int mul(int x, int y) {
return (int) ((long long) x * y % md);
}
inline int inv(int a) {
int b = md, u = 0, v = 1;
while (a) {
int t = b / a;
b -= t * a;
swap(a, b);
u -= t * v;
swap(u, v);
}
if (u < 0) {
u += md;
}
return u;
}
long long sum(int n) {
return (long long) n * (n + 1) / 2;
}
int main() {
#ifdef wxh010910
freopen("input.txt", "r", stdin);
#endif
ios::sync_with_stdio(false);
cin.tie(0);
cout.tie(0);
int tt;
cin >> tt;
while (tt--) {
int m;
cin >> m;
int l = 1, r = m;
while (l < r) {
int mid = (l + r + 1) >> 1;
if (sum(mid - 1) + sum(m - mid) >= (long long) mid * m) {
l = mid;
} else {
r = mid - 1;
}
}
int ans = (m + l + 1) % md;
sub(ans, mul(mul(l, l + 1), inv(m - l)));
ans = mul(ans, (md + 1) / 4);
cout << ans << "\n";
}
return 0;
}
将问题转化成有子串关系的对数。注意到后缀自动机一个节点对应的串可以表示成 [ l , r ] [l, r] [l,r] ,其中 $ l\in [a, b], r=c$ 。我们需要这些区间的本质不同子串个数。区间本质不同子串个数是经典问题,注意到其做法的线段树可以维护这个问题的左端点在一个区间,右端点固定的答案,直接维护就好了。
#include
using namespace std;
const int N = 223456;
int n, cnt, total, ed[N], pr[N], len[N], pos[N], nxt[N][26];
vector<pair<int, int>> events[N];
pair<int, int> modifies[N];
unsigned long long res[N];
vector<int> adj[N];
char s[N];
int extend(int p, int w) {
int np = ++total;
len[np] = len[p] + 1;
while (p && !nxt[p][w]) {
nxt[p][w] = np;
p = pr[p];
}
if (!p) {
pr[np] = 1;
} else {
int q = nxt[p][w];
if (len[q] == len[p] + 1) {
pr[np] = q;
} else {
int nq = ++total;
len[nq] = len[p] + 1;
memcpy(nxt[nq], nxt[q], sizeof nxt[nq]);
pr[nq] = pr[q];
pr[q] = pr[np] = nq;
while (p && nxt[p][w] == q) {
nxt[p][w] = nq;
p = pr[p];
}
}
}
return np;
}
namespace lct {
int f[N], tag[N], last[N], c[N][2];
void mark(int x, int v) {
tag[x] = last[x] = v;
}
void push(int x) {
if (tag[x]) {
if (c[x][0]) {
mark(c[x][0], tag[x]);
}
if (c[x][1]) {
mark(c[x][1], tag[x]);
}
tag[x] = 0;
}
}
bool is_root(int x) {
return c[f[x]][0] != x && c[f[x]][1] != x;
}
void rotate(int x) {
int y = f[x], z = f[y], k = c[y][1] == x;
if (!is_root(y)) {
c[z][c[z][1] == y] = x;
}
f[c[y][k] = c[x][!k]] = y;
f[f[c[x][!k] = y] = x] = z;
}
void splay(int x) {
static int st[N];
int top = 0;
st[++top] = x;
for (int i = x; !is_root(i); i = f[i]) {
st[++top] = f[i];
}
while (top) {
push(st[top--]);
}
while (!is_root(x)) {
int y = f[x], z = f[y];
if (!is_root(y)) {
rotate((c[y][1] == x) == (c[z][1] == y) ? y : x);
}
rotate(x);
}
}
void access(int x, int v) {
int t = 0;
cnt = 0;
while (x) {
splay(x);
modifies[++cnt] = make_pair(len[x], last[x]);
c[x][1] = t;
mark(x, v);
t = x;
x = f[x];
}
}
}
namespace segtree {
unsigned long long add[N], sum[N], sum2[N];
void apply(int x, int l, int r, unsigned long long v) {
add[x] += v;
sum[x] += v * (r - l + 1);
sum2[x] += v * ((unsigned long long) (r - l + 1) * (l + r) / 2);
}
void pull(int x, int z) {
sum[x] = sum[x + 1] + sum[z];
sum2[x] = sum2[x + 1] + sum2[z];
}
void push(int x, int l, int r) {
int y = (l + r) >> 1, z = x + ((y - l + 1) << 1);
if (add[x]) {
apply(x + 1, l, y, add[x]);
apply(z, y + 1, r, add[x]);
add[x] = 0;
}
}
void modify(int x, int l, int r, int ll, int rr, int v) {
if (ll <= l && r <= rr) {
apply(x, l, r, v);
} else {
int y = (l + r) >> 1, z = x + ((y - l + 1) << 1);
push(x, l, r);
if (ll <= y) {
modify(x + 1, l, y, ll, rr, v);
}
if (rr > y) {
modify(z, y + 1, r, ll, rr, v);
}
pull(x, z);
}
}
unsigned long long query_sum(int x, int l, int r, int ll, int rr) {
if (ll <= l && r <= rr) {
return sum[x];
} else {
int y = (l + r) >> 1, z = x + ((y - l + 1) << 1);
unsigned long long ans = 0;
push(x, l, r);
if (ll <= y) {
ans += query_sum(x + 1, l, y, ll, rr);
}
if (rr > y) {
ans += query_sum(z, y + 1, r, ll, rr);
}
return ans;
}
}
unsigned long long query_sum2(int x, int l, int r, int ll, int rr) {
if (ll <= l && r <= rr) {
return sum2[x];
} else {
int y = (l + r) >> 1, z = x + ((y - l + 1) << 1);
unsigned long long ans = 0;
push(x, l, r);
if (ll <= y) {
ans += query_sum2(x + 1, l, y, ll, rr);
}
if (rr > y) {
ans += query_sum2(z, y + 1, r, ll, rr);
}
return ans;
}
}
}
void dfs(int x) {
for (auto y : adj[x]) {
dfs(y);
ed[x] = ed[y];
}
}
int main() {
#ifdef wxh010910
freopen("input.txt", "r", stdin);
#endif
scanf("%s", s + 1);
n = strlen(s + 1);
pos[0] = ++total;
for (int i = 1; i <= n; ++i) {
pos[i] = extend(pos[i - 1], s[i] - 'a');
ed[pos[i]] = i;
}
for (int i = 2; i <= total; ++i) {
adj[pr[i]].push_back(i);
}
dfs(1);
for (int i = 2; i <= total; ++i) {
events[ed[i]].emplace_back(ed[i] - len[i] + 1, ed[i] - len[pr[i]]);
}
unsigned long long ans = 0;
for (int i = 2; i <= total; ++i) {
ans += len[i] - len[pr[i]];
}
if (ans % 2 == 0) {
ans = ans / 2 * (ans + 1);
} else {
ans = (ans + 1) / 2 * ans;
}
for (int i = 1; i <= total; ++i) {
lct::f[i] = pr[i];
}
for (int i = 1; i <= n; ++i) {
segtree::modify(1, 1, n, 1, i, 1);
lct::access(pos[i], i);
int last = 0;
for (int j = cnt; j > 1; --j) {
pair<int, int> p = modifies[j];
if (p.first) {
if (p.second) {
segtree::modify(1, 1, n, p.second - p.first + 1, p.second - last, -1);
}
last = p.first;
}
}
for (auto p : events[i]) {
int l = p.first, r = p.second;
ans -= segtree::query_sum2(1, 1, n, l, r);
ans += segtree::query_sum(1, 1, n, l, r) * (l - 1);
if (r != i) {
ans -= segtree::query_sum(1, 1, n, r + 1, i) * (r - l + 1);
}
}
}
printf("%llu\n", ans);
return 0;
}
当没有 ?
的时候,即为前 m − 1 m-1 m−1 行和后 m − 1 m-1 m−1 行匹配的方案数。有 ?
可以枚举 ?
是啥,然后减掉多算的。
#include
using namespace std;
const int MAX = 128;
const int md = (int) 1e9 + 7;
const int base = 2333;
const int md0 = (int) 1e9 + 7;
const int md1 = (int) 1e9 + 9;
struct hashv {
int h0, h1;
hashv(int h0 = 0, int h1 = 0): h0(h0), h1(h1) {
}
hashv operator * (const int &x) const {
return hashv((long long) h0 * x % md0, (long long) h1 * x % md1);
}
hashv operator + (const hashv &x) const {
return hashv((h0 + x.h0) % md0, (h1 + x.h1) % md1);
};
hashv operator - (const hashv &x) const {
return hashv((h0 + md0 - x.h0) % md0, (h1 + md1 - x.h1) % md1);
};
hashv operator * (const hashv &x) const {
return hashv((long long) h0 * x.h0 % md0, (long long) h1 * x.h1 % md1);
}
inline long long get() {
return (long long) h0 * md1 + h1;
}
};
inline void add(int &x, int y) {
x += y;
if (x >= md) {
x -= md;
}
}
inline void sub(int &x, int y) {
x -= y;
if (x < 0) {
x += md;
}
}
inline int mul(int x, int y) {
return (int) ((long long) x * y % md);
}
int main() {
#ifdef wxh010910
freopen("input.txt", "r", stdin);
#endif
int n, m;
cin >> m >> n;
vector<string> board(m);
vector<int> least(MAX);
vector<int> pos(m, -1);
for (int i = 0; i < m; ++i) {
cin >> board[i];
vector<int> cnt(MAX);
for (int j = 0; j < n; ++j) {
if (board[i][j] == '?') {
pos[i] = j;
} else {
++cnt[board[i][j]];
}
}
for (int j = 0; j < MAX; ++j) {
least[j] = max(least[j], cnt[j]);
}
}
int sum_least = 0;
for (int i = 0; i < MAX; ++i) {
sum_least += least[i];
}
if (sum_least > n) {
cout << -1 << "\n";
return 0;
}
for (int i = 0; i < m; ++i) {
vector<int> cnt(MAX);
for (int j = 0; j < n; ++j) {
if (board[i][j] != '?') {
++cnt[board[i][j]];
}
}
for (int j = 0; j < MAX; ++j) {
if (cnt[j] != least[j]) {
board[i][pos[i]] = j;
pos[i] = -1;
}
}
}
vector<hashv> power(m);
power[0] = hashv(1, 1);
for (int i = 1; i < m; ++i) {
power[i] = power[i - 1] * base;
}
vector<hashv> up(n), down(n);
for (int i = 0; i + 1 < m; ++i) {
for (int j = 0; j < n; ++j) {
if (board[i][j] != '?') {
up[j] = up[j] + power[i] * board[i][j];
}
if (board[i + 1][j] != '?') {
down[j] = down[j] + power[i] * board[i + 1][j];
}
}
}
auto solve = [&](int question) {
vector<hashv> new_up = up, new_down = down;
for (int i = 0; i + 1 < m; ++i) {
if (pos[i] != -1) {
new_up[pos[i]] = new_up[pos[i]] + power[i] * question;
}
if (pos[i + 1] != -1) {
new_down[pos[i + 1]] = new_down[pos[i + 1]] + power[i] * question;
}
}
unordered_map<long long, int> s;
int ans = 1;
for (int i = 0; i < n; ++i) {
++s[new_up[i].get()];
}
for (int i = 0; i < n; ++i) {
ans = mul(ans, s[new_down[i].get()]--);
}
return ans;
};
int ans = 0;
for (int i = 33; i <= 126; ++i) {
if (i != 63) {
add(ans, solve(i));
}
}
sub(ans, mul(solve(63), 92));
cout << ans << "\n";
return 0;
}
答案是 a 1 − a 2 a_1 - a_2 a1−a2 。
#include
using namespace std;
const int md = (int) 1e9 + 7;
int main() {
#ifdef wxh010910
freopen("input.txt", "r", stdin);
#endif
int foo, bar, baz;
cin >> foo >> bar >> baz;
cout << (bar + md - baz) % md << "\n";
return 0;
}