fish
枚举 A D AD AD , B C BC BC 的限制相当于 A D AD AD 是 B C BC BC 中垂线且 B C BC BC 中点在 A D AD AD 上,预处理所有中垂线,排序之后二分即可; E F EF EF 的限制可以双指针扫一下。
#include <bits/stdc++.h>
using namespace std;
struct point {
int x, y;
point(int x = 0, int y = 0): x(x), y(y) {
}
point operator + (const point &other) const {
return point(x + other.x, y + other.y);
}
point operator - (const point &other) const {
return point(x - other.x, y - other.y);
}
bool operator < (const point &other) const {
return x < other.x || (x == other.x && y < other.y);
}
long long len() const {
return (long long) x * x + (long long) y * y;
}
int where() const {
return y > 0 || (y == 0 && x < 0);
}
};
long long dot(point a, point b) {
return (long long) a.x * b.x + (long long) a.y * b.y;
}
long long cross(point a, point b) {
return (long long) a.x * b.y - (long long) a.y * b.x;
}
struct line {
point v;
point p;
line(point v, point p): v(v), p(p) {
}
bool operator < (const line &other) const {
if (v < other.v || other.v < v) {
return v < other.v;
} else if (dot(v, p) != dot(other.v, other.p)) {
return dot(v, p) < dot(other.v, other.p);
} else {
return p < other.p;
}
}
};
point norm(point p) {
int d = __gcd(p.x, p.y);
p.x /= d;
p.y /= d;
if (p.x < 0 || (p.x == 0 && p.y < 0)) {
p.x *= -1;
p.y *= -1;
}
return p;
}
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<point> p(n);
for (int i = 0; i < n; ++i) {
cin >> p[i].x >> p[i].y;
}
vector<line> lines;
for (int i = 0; i < n; ++i) {
for (int j = 0; j < i; ++j) {
lines.emplace_back(norm(p[i] - p[j]), p[i] + p[j]);
}
}
sort(lines.begin(), lines.end());
long long ans = 0;
for (int i = 0; i < n; ++i) {
vector<point> q;
for (int j = 0; j < n; ++j) {
if (j != i) {
q.push_back(p[j] - p[i]);
}
}
sort(q.begin(), q.end(), [&](const point &a, const point &b) {
if (a.where() != b.where()) {
return a.where() < b.where();
} else {
return cross(a, b) > 0;
}
});
for (int j = 0; j < n - 1; ++j) {
q.push_back(q[j]);
}
unordered_map<long long, int> cnt;
int ways_ef = 0;
for (int j = 0, l = 0, r = 0; j < n - 1; ++j) {
while (cross(q[j], q[r]) > 0 || (cross(q[j], q[r]) == 0 && r < n - 1) || dot(q[j], q[r]) < 0) {
ways_ef += cnt[q[r++].len()]++;
}
while (l < r && dot(q[j], q[l]) >= 0) {
ways_ef -= --cnt[q[l++].len()];
}
if (ways_ef) {
point foo = norm(point(q[j].y, -q[j].x)), bar = p[i] + p[i], baz = (p[i] + q[j]) + (p[i] + q[j]);
if (baz < bar) {
swap(bar, baz);
}
int ways_bc = lower_bound(lines.begin(), lines.end(), line(foo, baz)) - upper_bound(lines.begin(), lines.end(), line(foo, bar));
ans += (long long) ways_bc * ways_ef;
}
}
}
cout << ans * 4 << "\n";
return 0;
}
jojo
离线建树,然后暴力kmp跳fail,fail可以划分成若干段等差数列,注意失配的时候需要用到这样一个性质:假设周期是 l l l ,对于 i > 2 l i>2l i>2l , f a i l i = i − l fail_i = i-l faili=i−l ,失配的时候特判掉这种情况,复杂度就对了。具体证明可以参见吴瑾昭集训队论文。
#include <bits/stdc++.h>
using namespace std;
const int md = 998244353;
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> type(n + 1);
vector<int> repeat(n + 1);
vector<char> letter(n + 1);
vector<vector<int>> adj(n + 1);
for (int i = 1; i <= n; ++i) {
cin >> type[i];
if (type[i] == 1) {
cin >> repeat[i] >> letter[i];
adj[i - 1].push_back(i);
} else {
int from;
cin >> from;
adj[from].push_back(i);
}
}
vector<int> foo(1), bar(1), goes(1), right(1);
vector<long long> sum(1);
vector<char> let(1);
int m = 0;
auto find = [&](int p) {
return lower_bound(right.begin(), right.end(), p) - right.begin();
};
auto get_fail = [&](int p) {
int q = find(p);
return foo[q] * p + bar[q];
};
auto get_letter = [&](int p) {
return let[find(p)];
};
auto extend = [&](char letter, int repeat) {
int fail = foo[m] * right[m] + bar[m];
int l = right[m] + 1, r = right[m] + repeat;
if (!m) {
foo.push_back(1);
bar.push_back(-1);
goes.push_back(r);
right.push_back(r);
sum.push_back((long long) r * (r - 1) / 2);
let.push_back(letter);
++m;
} else {
int last = -1;
for (int i = l; i <= r; ++i) {
while (fail && letter != get_letter(fail + 1)) {
int fail_of_fail = get_fail(fail);
if (fail_of_fail * 2 < fail) {
fail = fail_of_fail;
} else {
int half = fail / 2;
fail = fail_of_fail;
if (letter != get_letter(fail + 1)) {
fail_of_fail = get_fail(fail);
fail = min(fail_of_fail, fail - half / (fail - fail_of_fail) * (fail - fail_of_fail));
}
}
}
if (!fail && letter != get_letter(fail + 1)) {
foo.push_back(0);
bar.push_back(0);
goes.push_back(r);
right.push_back(r);
sum.push_back(sum.back());
let.push_back(letter);
++m;
return;
}
++fail;
if (fail == last) {
foo.push_back(0);
bar.push_back(fail);
goes.push_back(r);
right.push_back(r);
sum.push_back(sum.back() + (long long) fail * (r - i + 1));
let.push_back(letter);
++m;
return;
}
int len = min(r - i, goes[find(fail)] - fail);
foo.push_back(1);
bar.push_back(fail - i);
goes.push_back(r);
right.push_back(i + len);
sum.push_back(sum.back() + (long long) (i * 2 + len) * (len + 1) / 2 + (long long) (len + 1) * (fail - i));
let.push_back(letter);
++m;
i += len;
fail += len;
last = fail;
}
}
};
vector<int> ans(n + 1);
function<void(int)> dfs = [&](int x) {
int old = m;
if (type[x] == 1) {
extend(letter[x], repeat[x]);
}
ans[x] = sum[m] % md;
for (auto y : adj[x]) {
dfs(y);
}
while (m > old) {
let.pop_back();
sum.pop_back();
foo.pop_back();
bar.pop_back();
goes.pop_back();
right.pop_back();
--m;
}
};
dfs(0);
for (int i = 1; i <= n; ++i) {
cout << ans[i] << "\n";
}
return 0;
}
polygon
终止态是所有边都连向 n n n ,不难发现第一问一定可以取到下界:没有连向 n n n 的边数。同时可以发现连向 n n n 的边把这个图划分成了若干棵树,相当于求树的拓扑序方案数,显然答案是 n ! / ∏ s i z e i n!/\prod size_i n!/∏sizei ,直接维护即可。
#include <bits/stdc++.h>
using namespace std;
const int md = (int) (1e9 + 7);
inline int mul(int x, int y) {
return (int) ((long long) x * y % md);
}
int main() {
#ifdef wxh010910
freopen("input.txt", "r", stdin);
#endif
ios::sync_with_stdio(false);
cin.tie(0);
cout.tie(0);
int type;
cin >> type;
int n;
cin >> n;
vector<int> invs(n + 1);
invs[1] = 1;
for (int i = 2; i <= n; ++i) {
invs[i] = mul(md - md / i, invs[md % i]);
}
vector<vector<int>> adj(n);
int turn = 0, ways = 1;
for (int i = 0; i < n - 3; ++i) {
int from, to;
cin >> from >> to;
--from;
--to;
if (from > to) {
swap(from, to);
}
adj[from].push_back(to);
adj[to].push_back(from);
if (to != n - 1) {
ways = mul(ways, ++turn);
ways = mul(ways, invs[to - from - 1]);
}
}
for (int i = 0; i < n; ++i) {
adj[i].push_back((i + 1) % n);
adj[(i + 1) % n].push_back(i);
}
cout << turn;
if (type) {
cout << " " << ways;
}
cout << "\n";
for (int i = 0; i < n; ++i) {
sort(adj[i].begin(), adj[i].end());
}
int m;
cin >> m;
while (m--) {
int a, b, c, d;
cin >> a >> c;
--a;
--c;
b = *upper_bound(adj[c].begin(), adj[c].end(), a);
d = *upper_bound(adj[a].begin(), adj[a].end(), c);
cout << turn - (d == n - 1);
if (type) {
cout << " " << mul(ways, mul(c - a - 1, d == n - 1 ? invs[turn] : invs[d - b - 1]));
}
cout << "\n";
}
return 0;
}
tour
对同色边和异色边分别保留一棵生成树,如果不是二分图再加一个自环,可以证明答案不变。然后暴力DP即可。
#include <bits/stdc++.h>
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, m, tt;
cin >> n >> m >> tt;
string s;
cin >> s;
vector<vector<vector<int>>> adj(2, vector<vector<int>>(n));
while (m--) {
int from, to;
cin >> from >> to;
--from;
--to;
adj[s[from] != s[to]][from].push_back(to);
adj[s[from] != s[to]][to].push_back(from);
}
vector<vector<vector<int>>> graph(2, vector<vector<int>>(n));
for (int rep = 0; rep < 2; ++rep) {
vector<int> color(n, -1);
for (int i = 0; i < n; ++i) {
if (color[i] == -1) {
queue<int> q;
q.push(i);
color[i] = 0;
bool add = false;
while (!q.empty()) {
int x = q.front();
q.pop();
for (auto y : adj[rep][x]) {
if (color[y] == -1) {
graph[rep][x].push_back(y);
graph[rep][y].push_back(x);
color[y] = !color[x];
q.push(y);
} else if (color[x] == color[y] && !add) {
graph[rep][i].push_back(i);
add = true;
}
}
}
}
}
}
vector<vector<bool>> dp(n, vector<bool>(n));
queue<pair<int, int>> q;
for (int i = 0; i < n; ++i) {
dp[i][i] = true;
q.emplace(i, i);
}
for (int x = 0; x < n; ++x) {
for (auto y : adj[0][x]) {
dp[x][y] = true;
q.emplace(x, y);
}
}
while (!q.empty()) {
int x = q.front().first, y = q.front().second;
q.pop();
for (int rep = 0; rep < 2; ++rep) {
for (auto xx : graph[rep][x]) {
for (auto yy : graph[rep][y]) {
if (!dp[xx][yy]) {
dp[xx][yy] = true;
q.emplace(xx, yy);
}
}
}
}
}
while (tt--) {
int from, to;
cin >> from >> to;
--from;
--to;
cout << (dp[from][to] ? "YES" : "NO") << "\n";
}
return 0;
}
dance
考虑idft的定义式: a n s i = 1 k ∑ j = 0 k − 1 ω k − i j F ( ω k j ) ans_i = \frac{1}{k} \sum_{j=0}^{k-1} \omega_k^{-ij} F(\omega_k^j) ansi=k1∑j=0k−1ωk−ijF(ωkj) ,用矩阵快速幂求出点值,然后搞个卷积就行了。
#include <bits/stdc++.h>
using namespace std;
int md;
inline void add(int &x, int y) {
x += y;
if (x >= md) {
x -= md;
}
}
inline int mul(int x, int y) {
return (int) ((long long) x * y % md);
}
inline int power(int x, int y) {
int res = 1;
while (y) {
if (y & 1) {
res = mul(res, x);
}
x = mul(x, x);
y >>= 1;
}
return res;
}
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;
}
namespace fft {
typedef double ld;
const ld pi = acos(-1);
struct num {
ld x, y;
num(ld x = 0, ld y = 0): x(x), y(y) {
}
num operator + (const num &b) const {
return num(x + b.x, y + b.y);
}
num operator - (const num &b) const {
return num(x - b.x, y - b.y);
}
num operator * (const num &b) const {
return num(x * b.x - y * b.y, x * b.y + y * b.x);
}
};
num conj(num a) {
return num(a.x, -a.y);
}
vector<num> fa, fb, roots = {num(0, 0), num(1, 0)};
vector<int> rev = {0, 1};
int base = 1;
void ensure_base(int nbase) {
if (nbase <= base) {
return;
}
rev.resize(1 << nbase);
for (int i = 0; i < (1 << nbase); ++i) {
rev[i] = rev[i >> 1] >> 1 | ((i & 1) << (nbase - 1));
}
roots.resize(1 << nbase);
while (base < nbase) {
ld angle = 2 * pi / (1 << (base + 1));
for (int i = 1 << (base - 1); i < 1 << base; ++i) {
roots[i << 1] = roots[i];
ld angle_i = angle * ((i << 1) + 1 - (1 << base));
roots[i << 1 | 1] = num(cos(angle_i), sin(angle_i));
}
++base;
}
}
void dft(vector<num> &a, int n) {
int zeros = __builtin_ctz(n);
ensure_base(zeros);
int shift = base - zeros;
for (int i = 0; i < n; ++i) {
if (i < rev[i] >> shift) {
swap(a[i], a[rev[i] >> shift]);
}
}
for (int i = 1; i < n; i <<= 1) {
for (int j = 0; j < n; j += i << 1) {
for (int k = 0; k < i; ++k) {
num x = a[j + k], y = a[j + k + i] * roots[i + k];
a[j + k] = x + y;
a[j + k + i] = x - y;
}
}
}
}
vector<int> multiply(const vector<int> &a, const vector<int> &b) {
int need = a.size() + b.size() - 1, nbase = 0;
while (1 << nbase < need) {
++nbase;
}
ensure_base(nbase);
bool equal = a == b;
int sz = 1 << nbase;
if (sz > (int) fa.size()) {
fa.resize(sz);
}
if (sz > (int) fb.size()) {
fb.resize(sz);
}
for (int i = 0; i < (int) a.size(); i++) {
int x = (a[i] % md + md) % md;
fa[i] = num(x & ((1 << 15) - 1), x >> 15);
}
for (int i = a.size(); i < sz; ++i) {
fa[i] = num(0, 0);
}
dft(fa, sz);
if (equal) {
for (int i = 0; i < sz; ++i) {
fb[i] = fa[i];
}
} else {
for (int i = 0; i < (int) b.size(); ++i) {
int x = (b[i] % md + md) % md;
fb[i] = num(x & ((1 << 15) - 1), x >> 15);
}
for (int i = b.size(); i < sz; ++i) {
fb[i] = num(0, 0);
}
dft(fb, sz);
}
ld ratio = 0.25 / sz;
num r1(1, 0);
num r2(0, -1);
num r3(ratio, 0);
num r4(0, -ratio);
num r5(0, 1);
for (int i = 0; i <= sz >> 1; ++i) {
int j = (sz - i) & (sz - 1);
num a1 = (fa[i] + conj(fa[j])) * r1;
num a2 = (fa[i] - conj(fa[j])) * r2;
num b1 = (fb[i] + conj(fb[j])) * r3;
num b2 = (fb[i] - conj(fb[j])) * r4;
if (i != j) {
num c1 = (fa[j] + conj(fa[i])) * r1;
num c2 = (fa[j] - conj(fa[i])) * r2;
num d1 = (fb[j] + conj(fb[i])) * r3;
num d2 = (fb[j] - conj(fb[i])) * r4;
fa[i] = c1 * d1 + c2 * d2 * r5;
fb[i] = c1 * d2 + c2 * d1;
}
fa[j] = a1 * b1 + a2 * b2 * r5;
fb[j] = a1 * b2 + a2 * b1;
}
dft(fa, sz);
dft(fb, sz);
vector<int> c(need);
for (int i = 0; i < need; i++) {
long long aa = fa[i].x + 0.5;
long long bb = fb[i].x + 0.5;
long long cc = fa[i].y + 0.5;
c[i] = (aa + (bb % md << 15) + (cc % md << 30)) % md;
}
return c;
}
}
using fft::multiply;
struct matrix {
vector<vector<int>> a;
int n;
matrix(int n): n(n) {
a = vector<vector<int>>(n, vector<int>(n, 0));
}
matrix operator * (const matrix &other) const {
matrix ans(n);
for (int k = 0; k < n; ++k) {
for (int i = 0; i < n; ++i) {
for (int j = 0; j < n; ++j) {
add(ans.a[i][j], mul(a[i][k], other.a[k][j]));
}
}
}
return ans;
}
};
int main() {
#ifdef wxh010910
freopen("input.txt", "r", stdin);
#endif
ios::sync_with_stdio(false);
cin.tie(0);
cout.tie(0);
int n, m, e, x, y;
cin >> n >> m >> e >> x >> y >> md;
--x;
--y;
matrix a(n);
for (int i = 0; i < n; ++i) {
for (int j = 0; j < n; ++j) {
cin >> a.a[i][j];
}
}
int root = -1;
{
vector<int> primes;
int temp = md - 1;
for (int i = 2; i * i <= temp; ++i) {
if (temp % i == 0) {
primes.push_back(i);
while (temp % i == 0) {
temp /= i;
}
}
}
if (temp != 1) {
primes.push_back(temp);
}
root = 2;
while (true) {
bool can = true;
for (auto p : primes) {
if (power(root, (md - 1) / p) == 1) {
can = false;
break;
}
}
if (can) {
break;
}
++root;
}
root = power(root, (md - 1) / m);
}
int inv_root = inv(root);
vector<int> f(m);
for (int i = 0, w = 1; i < m; ++i, w = mul(w, root)) {
matrix b(n), ans(n);
for (int j = 0; j < n; ++j) {
for (int k = 0; k < n; ++k) {
b.a[j][k] = mul(a.a[j][k], w);
if (j == k) {
add(b.a[j][k], 1);
ans.a[j][k] = 1;
}
}
}
int temp = e;
while (temp) {
if (temp & 1) {
ans = ans * b;
}
b = b * b;
temp >>= 1;
}
f[i] = ans.a[x][y];
}
vector<int> fact(m * 2 - 1), inv_fact(m * 2 - 1);
fact[0] = inv_fact[0] = 1;
for (int i = 1, w = 1, inv_w = 1; i < m * 2 - 1; ++i, w = mul(w, root), inv_w = mul(inv_w, inv_root)) {
fact[i] = mul(fact[i - 1], w);
inv_fact[i] = mul(inv_fact[i - 1], inv_w);
}
vector<int> foo(m * 2 - 1);
for (int i = 0; i < m * 2 - 1; ++i) {
foo[i] = inv_fact[i];
}
vector<int> bar(m);
for (int i = 0; i < m; ++i) {
bar[i] = mul(f[i], fact[i]);
}
reverse(bar.begin(), bar.end());
vector<int> ans = multiply(foo, bar);
int inv_m = inv(m);
for (int i = 0; i < m; ++i) {
cout << mul(inv_m, mul(ans[i + m - 1], fact[i])) << "\n";
}
return 0;
}
sequence
没有修改的做法:维护关于平均数的单调栈,参见高睿泉集训队论文。
有修改的话,维护前后缀单调栈,二分套二分即可。
#include <bits/stdc++.h>
using namespace std;
const int md = 998244353;
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
ios::sync_with_stdio(false);
cin.tie(0);
cout.tie(0);
int n, m;
cin >> n >> m;
vector<int> a(n);
for (int i = 0; i < n; ++i) {
cin >> a[i];
}
vector<long long> sum(n + 1);
vector<int> sqr(n + 1);
for (int i = 0; i < n; ++i) {
sum[i + 1] = sum[i] + a[i];
add(sqr[i + 1] = sqr[i], mul(a[i], a[i]));
}
vector<int> invs(n + 1);
invs[0] = invs[1] = 1;
for (int i = 2; i <= n; ++i) {
invs[i] = mul(md - md / i, invs[md % i]);
}
int foo = -1, bar = 0, baz = 0;
auto get_sum = [&](int l, int r) {
return sum[r] - sum[l] + (l < foo && foo <= r) * bar;
};
auto get_sqr = [&](int l, int r) {
int ans = baz;
add(ans, sqr[r]);
sub(ans, sqr[l]);
return ans;
};
auto check = [&](int l, int r, int ll, int rr) {
return (__int128) get_sum(l, r) * (rr - ll) < (__int128) get_sum(ll, rr) * (r - l);
};
auto get = [&](int l, int r) {
int x = mul(get_sum(l, r) % md, invs[r - l]);
int ans = 0;
add(ans, mul(r - l, mul(x, x)));
sub(ans, mul(2, mul(get_sum(l, r) % md, x)));
add(ans, get_sqr(l, r));
return ans;
};
vector<vector<pair<int, int>>> events(n + 1);
vector<int> ans(m);
for (int i = 0; i < m; ++i) {
int foo, bar;
cin >> foo >> bar;
events[foo].emplace_back(bar, i);
}
vector<vector<int>> erased(n);
vector<int> suff(n + 1);
vector<int> stack_suff(1, n);
int top_suff = 0;
for (int i = n - 1; ~i; --i) {
suff[i] = suff[i + 1];
while (top_suff && check(stack_suff[top_suff], stack_suff[top_suff - 1], i, stack_suff[top_suff])) {
sub(suff[i], get(stack_suff[top_suff], stack_suff[top_suff - 1]));
erased[i].push_back(stack_suff[top_suff]);
stack_suff.pop_back();
--top_suff;
}
add(suff[i], get(i, stack_suff[top_suff]));
stack_suff.push_back(i);
++top_suff;
}
vector<int> pref(n + 1);
vector<int> stack_pref(1, 0);
int top_pref = 0;
for (int i = 1; i <= n; ++i) {
stack_suff.pop_back();
--top_suff;
reverse(erased[i - 1].begin(), erased[i - 1].end());
for (auto j : erased[i - 1]) {
stack_suff.push_back(j);
++top_suff;
}
for (auto p : events[i]) {
foo = i;
bar = p.first - a[i - 1];
add(baz, mul(p.first, p.first));
sub(baz, mul(a[i - 1], a[i - 1]));
auto query_left = [&](int rr) {
int l = 0, r = top_pref;
while (l < r) {
int mid = (l + r) >> 1;
int ll = stack_pref[mid];
if (check(ll, rr, stack_pref[mid], stack_pref[mid + 1])) {
r = mid;
} else {
l = mid + 1;
}
}
return stack_pref[r];
};
auto query_right = [&]() {
int l = 0, r = top_suff;
while (l < r) {
int mid = (l + r) >> 1;
int rr = stack_suff[mid];
int ll = query_left(rr);
if (check(stack_suff[mid + 1], stack_suff[mid], ll, rr)) {
r = mid;
} else {
l = mid + 1;
}
}
return stack_suff[r];
};
int r = query_right();
int l = query_left(r);
add(ans[p.second], get(l, r));
add(ans[p.second], pref[l]);
add(ans[p.second], suff[r]);
foo = -1;
bar = 0;
baz = 0;
}
pref[i] = pref[i - 1];
while (top_pref && check(stack_pref[top_pref], i, stack_pref[top_pref - 1], stack_pref[top_pref])) {
sub(pref[i], get(stack_pref[top_pref - 1], stack_pref[top_pref]));
stack_pref.pop_back();
--top_pref;
}
add(pref[i], get(stack_pref[top_pref], i));
stack_pref.push_back(i);
++top_pref;
}
cout << pref[n] << "\n";
for (int i = 0; i < m; ++i) {
cout << ans[i] << "\n";
}
return 0;
}