建出trie树,那么匹配的东西一定是祖先关系。
记 f ( x , a , b ) f(x,a,b) f(x,a,b) 表示考虑了 x x x 的子树,祖先有 a a a 个小号匹配子树,子树有 b b b 个小号匹配祖先的方案数,背包转移即可。
#include
using namespace std;
const int N = 23456;
const int ALPHA = 26;
const int md = 1e9 + 7;
int n, m, root, total, type[N], go[N][ALPHA], dp[N][11][21];
char str[N];
inline void add(int &x, int y) {
x += y;
if (x >= md) {
x -= md;
}
}
inline int mul(int x, int y) {
return (long long)x * y % md;
}
inline int new_node() {
++total;
for (int i = 0; i < ALPHA; ++i) {
go[total][i] = 0;
}
type[total] = 0;
return total;
}
void dfs(int x) {
static int temp[11][21];
memset(dp[x], 0, sizeof dp[x]);
dp[x][0][0] = 1;
for (int i = 0; i < ALPHA; ++i) {
int y = go[x][i];
if (y) {
dfs(y);
}
}
for (int i = 0; i < ALPHA; ++i) {
int y = go[x][i];
if (y) {
for (int a = 0; a <= 10; ++a) {
for (int b = 0; b <= 20; ++b) {
if (!dp[x][a][b]) {
continue;
}
for (int aa = 0; aa <= 10 - a; ++aa) {
for (int bb = 0; bb <= 20 - b; ++bb) {
if (dp[y][aa][bb]) {
add(temp[a + aa][b + bb], mul(dp[x][a][b], dp[y][aa][bb]));
}
}
}
}
}
for (int a = 0; a <= 10; ++a) {
for (int b = 0; b <= 20; ++b) {
dp[x][a][b] = temp[a][b];
temp[a][b] = 0;
}
}
}
}
if (type[x] == 1) {
for (int a = 0; a <= 10; ++a) {
for (int b = 0; b <= 20; ++b) {
if (!dp[x][a][b]) {
continue;
}
add(temp[a][b], dp[x][a][b]);
if (a + 1 <= 10) {
add(temp[a + 1][b], dp[x][a][b]);
}
if (b - 1 >= 0) {
add(temp[a][b - 1], mul(b, dp[x][a][b]));
}
if (a + 2 <= 10) {
add(temp[a + 2][b], mul(md + 1 >> 1, dp[x][a][b]));
}
if (a + 1 <= 10 && b - 1 >= 0) {
add(temp[a + 1][b - 1], mul(b, dp[x][a][b]));
}
if (b - 2 >= 0) {
add(temp[a][b - 2], mul(b * (b - 1) / 2, dp[x][a][b]));
}
}
}
for (int a = 0; a <= 10; ++a) {
for (int b = 0; b <= 20; ++b) {
dp[x][a][b] = temp[a][b];
temp[a][b] = 0;
}
}
} else if (type[x] == 2) {
for (int a = 0; a <= 10; ++a) {
for (int b = 0; b <= 20; ++b) {
if (!dp[x][a][b]) {
continue;
}
add(temp[a][b], dp[x][a][b]);
if (a - 1 >= 0) {
add(temp[a - 1][b], mul(a, dp[x][a][b]));
}
if (b + 1 <= 20) {
add(temp[a][b + 1], dp[x][a][b]);
}
}
}
for (int a = 0; a <= 10; ++a) {
for (int b = 0; b <= 20; ++b) {
dp[x][a][b] = temp[a][b];
temp[a][b] = 0;
}
}
}
}
int main() {
#ifdef wxh010910
freopen("input.txt", "r", stdin);
#endif
int tt;
scanf("%d", &tt);
for (int ttt = 1; ttt <= tt; ++ttt) {
scanf("%d %d", &n, &m);
total = 0;
root = new_node();
for (int i = 1; i <= n; ++i) {
scanf("%s", str);
int x = root;
for (int j = 0; str[j]; ++j) {
if (!go[x][str[j] - 'a']) {
go[x][str[j] - 'a'] = new_node();
}
x = go[x][str[j] - 'a'];
}
type[x] = 1;
}
for (int i = 1; i <= m; ++i) {
scanf("%s", str);
int x = root;
for (int j = 0; str[j]; ++j) {
if (!go[x][str[j] - 'a']) {
go[x][str[j] - 'a'] = new_node();
}
x = go[x][str[j] - 'a'];
}
type[x] = 2;
}
dfs(root);
printf("Case #%d: %d\n", ttt, dp[root][0][0]);
}
return 0;
}
先默认每个位置是最大概率的数,每次失配会让概率乘最多 1 2 \frac{1}{2} 21 ,用后缀数组暴力匹配,当概率小于 e p s eps eps 时直接返回 0 0 0 即可。
#include
using namespace std;
typedef double ld;
const int N = 623456;
const int LOG = 20;
const ld eps = 1e-12;
int n, m, low[N], high[N], prob[N][10];
ld sum[N];
struct suffix_array_t {
int n, s[N], rank[N], array[N], height[N], length[N], rmq[LOG][N];
void init() {
static int temp[N], first[N], second[N], number[N];
for (int i = 1; i <= n; ++i) {
++number[s[i]];
}
for (int i = 1; i <= n; ++i) {
number[i] += number[i - 1];
}
for (int i = 1; i <= n; ++i) {
array[number[s[i]]--] = i;
}
for (int i = 1; i <= n; ++i) {
number[i] = 0;
}
rank[array[1]] = 1;
for (int i = 2; i <= n; ++i) {
rank[array[i]] = rank[array[i - 1]] + (s[array[i]] != s[array[i - 1]]);
}
for (int length = 1; rank[array[n]] != n; length <<= 1) {
for (int i = 1; i <= n; ++i) {
++number[second[i] = i + length > n ? 0 : rank[i + length]];
}
for (int i = 1; i <= n; ++i) {
number[i] += number[i - 1];
}
for (int i = 1; i <= n; ++i) {
temp[number[second[i]]--] = i;
}
for (int i = 0; i <= n; ++i) {
number[i] = 0;
}
for (int i = 1; i <= n; ++i) {
++number[first[i] = rank[i]];
}
for (int i = 1; i <= n; ++i) {
number[i] += number[i - 1];
}
for (int i = n; i; --i) {
array[number[first[temp[i]]]--] = temp[i];
}
for (int i = 1; i <= n; ++i) {
number[i] = 0;
}
rank[array[1]] = 1;
for (int i = 2; i <= n; ++i) {
rank[array[i]] = rank[array[i - 1]] + (first[array[i]] != first[array[i - 1]] || second[array[i]] != second[array[i - 1]]);
}
}
for (int i = 1, j = 0; i <= n; ++i) {
for (j -= j > 0; rank[i] != 1 && i + j <= n && array[rank[i] - 1] + j <= n && s[i + j] == s[array[rank[i] - 1] + j]; ++j);
height[rank[i]] = j;
}
for (int i = 2; i <= n; ++i) {
length[i] = length[i >> 1] + 1;
}
for (int i = 1; i <= n; ++i) {
rmq[0][i] = height[i];
}
for (int i = 1; i < LOG; ++i) {
for (int j = 1; j + (1 << i) - 1 <= n; ++j) {
rmq[i][j] = min(rmq[i - 1][j], rmq[i - 1][j + (1 << i - 1)]);
}
}
}
int lcp(int x, int y) {
if (x == y) {
return n - x + 1;
}
x = rank[x];
y = rank[y];
if (x > y) {
swap(x, y);
}
++x;
int k = length[y - x];
return min(rmq[k][x], rmq[k][y - (1 << k) + 1]);
}
} suffix_array;
ld solve(int l, int r, int ll, int rr, ld value) {
if (value < eps) {
return 0;
}
if (l > r) {
return value;
}
int lcp = suffix_array.lcp(l, ll);
if (lcp >= r - l + 1) {
return value * exp(sum[r] - sum[l - 1]);
}
value *= exp(sum[l + lcp - 1] - sum[l - 1]);
l += lcp;
ll += lcp;
if (low[l] <= suffix_array.s[ll] && high[l] >= suffix_array.s[ll]) {
value *= prob[l][suffix_array.s[ll] - low[l]] * 1e-9;
return solve(l + 1, r, ll + 1, rr, value);
} else {
return 0;
}
}
int main() {
#ifdef wxh010910
freopen("input.txt", "r", stdin);
#endif
int tt;
scanf("%d", &tt);
for (int ttt = 1; ttt <= tt; ++ttt) {
printf("Case #%d:\n", ttt);
scanf("%d %d", &n, &m);
suffix_array.n = n + m;
for (int i = 1; i <= n; ++i) {
scanf("%d %d", &low[i], &high[i]);
int p = 0;
for (int j = low[i]; j <= high[i]; ++j) {
scanf("%d", &prob[i][j - low[i]]);
if (prob[i][j - low[i]] > prob[i][p]) {
p = j - low[i];
}
}
suffix_array.s[i] = p + low[i];
}
for (int i = 1; i <= m; ++i) {
scanf("%d", &suffix_array.s[i + n]);
}
suffix_array.init();
for (int i = 1; i <= n; ++i) {
sum[i] = sum[i - 1] + log(prob[i][suffix_array.s[i] - low[i]] * 1e-9);
}
for (int i = 1; i <= n - m + 1; ++i) {
printf("%.12lf\n", solve(i, i + m - 1, n + 1, n + m, 1));
}
}
return 0;
}
相当于 min ( n , k ) \min(n,k) min(n,k) 个位置是有序的,枚举最后的序列,这样的序列只有 O ( n 2 ) O(n^2) O(n2) 个,可以快速判断每个是否合法,最后答案乘上 k ! k! k! 即可。
#include
using namespace std;
int main() {
#ifdef wxh010910
freopen("input.txt", "r", stdin);
#endif
int tt;
scanf("%d", &tt);
for (int ttt = 1; ttt <= tt; ++ttt) {
int n, m, md;
scanf("%d %d %d", &n, &m, &md);
m = min(m, n);
int answer = 1;
for (int i = 1; i <= n; ++i) {
for (int j = 1; j <= n; ++j) {
if (j != i) {
if (j > m || (j == m && i > m)) {
++answer;
}
}
}
}
for (int i = m; i < n; ++i) {
--answer;
}
for (int i = 1; i <= m; ++i) {
answer = (long long)answer * i % md;
}
printf("Case #%d: %d\n", ttt, answer);
}
return 0;
}
边分治,闵可夫斯基和。
#include
using namespace std;
typedef long long ll;
const int N = 456789;
struct point_t {
ll x, y;
point_t(ll x = 0, ll y = 0):x(x), y(y) {
}
point_t operator + (const point_t &b) const {
return point_t(x + b.x, y + b.y);
}
point_t operator - (const point_t &b) const {
return point_t(x - b.x, y - b.y);
}
ll operator * (const point_t &b) const {
return x * b.y - y * b.x;
}
ll solve(int k) {
return k * x + y;
}
} dist[N];
vector<pair<int, point_t>> adj[N], edge[N];
int n, m, root, proot, size[N];
vector<point_t> l, r, all;
bool visit[N];
void rebuild(int x, int parent) {
vector<pair<int, point_t>> son;
for (auto e : edge[x]) {
if (e.first != parent) {
rebuild(e.first, x);
son.push_back(e);
}
}
for (int i = 0, t = x; i < son.size(); ++i) {
if (i && i + 1 < son.size()) {
adj[n].emplace_back(t, point_t(0, 0));
adj[t].emplace_back(n, point_t(0, 0));
t = n++;
}
adj[t].emplace_back(son[i].first, son[i].second);
adj[son[i].first].emplace_back(t, son[i].second);
}
}
void find_root(int x, int p, int s) {
size[x] = 1;
for (auto e : adj[x]) {
int y = e.first;
if (y != p && !visit[y]) {
find_root(y, x, s);
size[x] += size[y];
}
}
if (!~root || max(size[root], s - size[root]) > max(size[x], s - size[x])) {
root = x;
proot = p;
}
}
void init(int x, int p, bool subtree) {
if (subtree) {
r.push_back(dist[x]);
} else {
l.push_back(dist[x]);
}
for (auto e : adj[x]) {
int y = e.first;
if (y != p && !visit[y]) {
dist[y] = dist[x] + e.second;
init(y, x, y == root || subtree);
}
}
}
vector<point_t> build_convex(vector<point_t> v) {
vector<point_t> convex;
sort(v.begin(), v.end(), [&](const point_t &a, const point_t &b) {
return a.x < b.x || (a.x == b.x && a.y > b.y);
});
int m = 0;
for (int i = 0; i < v.size(); ++i) {
if (i && v[i].x == v[i - 1].x) {
continue;
}
while (m && convex[m - 1].y <= v[i].y) {
convex.pop_back();
--m;
}
while (m > 1 && (v[i] - convex[m - 2]) * (convex[m - 1] - convex[m - 2]) <= 0) {
convex.pop_back();
--m;
}
convex.push_back(v[i]);
m++;
}
return convex;
}
void minkowski_sum(vector<point_t> a, vector<point_t> b) {
all.push_back(a[0] + b[0]);
for (int i = 0, j = 0; i + 1 < a.size() || j + 1 < b.size(); ) {
if (j + 1 == b.size() || (i + 1 < a.size() && (a[i + 1] - a[i]) * (b[j + 1] - b[j]) < 0)) {
all.push_back(a[++i] + b[j]);
} else {
all.push_back(a[i] + b[++j]);
}
}
}
void divide(int x, int s) {
if (s == 1) {
return;
}
root = -1;
find_root(x, -1, s);
int u = root, v = proot, size_u = size[u], size_v = s - size[u];
dist[v] = point_t(0, 0);
l.clear();
r.clear();
init(v, -1, false);
l = build_convex(l);
r = build_convex(r);
minkowski_sum(l, r);
visit[v] = true;
divide(u, size_u);
visit[v] = false;
visit[u] = true;
divide(v, size_v);
visit[u] = false;
}
int main() {
#ifdef wxh010910
freopen("input.txt", "r", stdin);
#endif
int tt;
scanf("%d", &tt);
for (int ttt = 1; ttt <= tt; ++ttt) {
printf("Case #%d:\n", ttt);
scanf("%d %d", &n, &m);
for (int i = 0; i < n - 1; ++i) {
int x, y, a = 1, b;
scanf("%d %d %d", &x, &y, &b);
--x;
--y;
edge[x].emplace_back(y, point_t(a, b));
edge[y].emplace_back(x, point_t(a, b));
}
rebuild(0, -1);
divide(0, n);
all = build_convex(all);
vector<pair<int, int>> queries(m);
for (int i = 0; i < m; ++i) {
scanf("%d", &queries[i].first);
queries[i].second = i;
}
sort(queries.begin(), queries.end());
vector<ll> answer(m);
int j = 0;
for (auto p : queries) {
int i = p.first;
while (j + 1 < all.size() && all[j + 1].solve(i) >= all[j].solve(i)) {
++j;
}
answer[p.second] = all[j].solve(i);
}
for (int i = 0; i < m; ++i) {
printf("%lld\n", answer[i]);
}
all.clear();
for (int i = 0; i < n; ++i) {
adj[i].clear();
edge[i].clear();
visit[i] = false;
}
}
return 0;
}
线段树维护每个区间 x + y , x − y , − x + y , − x − y x+y,x-y,-x+y,-x-y x+y,x−y,−x+y,−x−y 的最大次大值即可。
#include
using namespace std;
typedef long long ll;
const int N = 123456;
struct info_t {
int fx, fy;
ll x, y;
info_t(ll x = 0, int fx = 0, ll y = 0, int fy = 0):x(x), fx(fx), y(y), fy(fy) {
}
};
struct node_t {
info_t pp, pn, np, nn;
ll answer;
} tree[N << 1];
int n, m, c[N];
ll x[N], y[N];
void update(info_t &info, ll x, int fx) {
if (~fx) {
if (info.fx == fx) {
if (info.x < x) {
info.x = x;
}
} else {
if (info.fy == -1 || info.y < x) {
info.y = x;
info.fy = fx;
}
if (info.y > info.x) {
swap(info.x, info.y);
swap(info.fx, info.fy);
}
}
}
}
info_t operator + (info_t a, info_t b) {
info_t result(a.x, a.fx, 0, -1);
update(result, a.y, a.fy);
update(result, b.x, b.fx);
update(result, b.y, b.fy);
return result;
}
node_t make(int p) {
node_t result;
result.pp = info_t(x[p] + y[p], c[p], 0, -1);
result.pn = info_t(x[p] - y[p], c[p], 0, -1);
result.np = info_t(-x[p] + y[p], c[p], 0, -1);
result.nn = info_t(-x[p] - y[p], c[p], 0, -1);
result.answer = 0;
return result;
}
ll merge(info_t l, info_t r) {
if (l.fx != r.fx) {
return l.x + r.x;
} else {
ll result = 0;
if (~l.fy) {
result = max(result, l.y + r.x);
}
if (~r.fy) {
result = max(result, r.y + l.x);
}
return result;
}
}
node_t unite(node_t l, node_t r) {
node_t result;
result.answer = max(l.answer, r.answer);
result.pp = l.pp + r.pp;
result.pn = l.pn + r.pn;
result.np = l.np + r.np;
result.nn = l.nn + r.nn;
result.answer = max(result.answer, merge(l.pp, r.nn));
result.answer = max(result.answer, merge(l.pn, r.np));
result.answer = max(result.answer, merge(l.np, r.pn));
result.answer = max(result.answer, merge(l.nn, r.pp));
return result;
}
void pull(int x, int z) {
tree[x] = unite(tree[x + 1], tree[z]);
}
void build(int x, int l, int r) {
if (l == r) {
tree[x] = make(l);
} else {
int y = l + r >> 1, z = x + (y - l + 1 << 1);
build(x + 1, l, y);
build(z, y + 1, r);
pull(x, z);
}
}
void modify(int x, int l, int r, int p) {
if (l == r) {
tree[x] = make(l);
} else {
int y = l + r >> 1, z = x + (y - l + 1 << 1);
if (p <= y) {
modify(x + 1, l, y, p);
} else {
modify(z, y + 1, r, p);
}
pull(x, z);
}
}
node_t query(int x, int l, int r, int ql, int qr) {
if (l == ql && r == qr) {
return tree[x];
} else {
int y = l + r >> 1, z = x + (y - l + 1 << 1);
if (qr <= y) {
return query(x + 1, l, y, ql, qr);
} else if (ql > y) {
return query(z, y + 1, r, ql, qr);
} else {
return unite(query(x + 1, l, y, ql, y), query(z, y + 1, r, y + 1, qr));
}
}
}
int main() {
#ifdef wxh010910
freopen("input.txt", "r", stdin);
#endif
int tt;
scanf("%d", &tt);
for (int ttt = 1; ttt <= tt; ++ttt) {
printf("Case #%d:\n", ttt);
scanf("%d %d", &n, &m);
for (int i = 1; i <= n; ++i) {
scanf("%lld %lld %d\n", &x[i], &y[i], &c[i]);
}
build(1, 1, n);
while (m--) {
int type;
scanf("%d", &type);
if (type == 1) {
int p, dx, dy;
scanf("%d %d %d", &p, &dx, &dy);
x[p] += dx;
y[p] += dy;
modify(1, 1, n, p);
} else if (type == 2) {
int p;
scanf("%d", &p);
scanf("%d", &c[p]);
modify(1, 1, n, p);
} else {
int l, r;
scanf("%d %d", &l, &r);
printf("%lld\n", query(1, 1, n, l, r).answer);
}
}
}
return 0;
}
暴力是DP做卷积。可以对 1 , 2 , ⋯   , 3 w 1, 2, \cdots, 3w 1,2,⋯,3w 代入多项式,这样DP就是点乘,最后插值回来即可。
#include
using namespace std;
const int N = 12345;
const int M = 1234;
const int md = 1e9 + 7;
int n, m, w, inv[M], answer[M], value[N], degree[N], power[M][M], dp[N][M][3];
vector<int> order, adj[N];
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 (long long)x * y % md;
}
vector<int> interpolation(vector<int> u) {
int n = u.size();
vector<int> result(n), sum(n);
result[0] = u[0];
sum[0] = 1;
for (int i = 1; i < n; ++i) {
for (int j = n - 1; j >= i; --j) {
sub(u[j], u[j - 1]);
u[j] = mul(u[j], inv[i]);
}
for (int j = i; ~j; --j) {
sum[j] = mul(sum[j], md + 1 - i);
if (j) {
add(sum[j], sum[j - 1]);
}
add(result[j], mul(sum[j], u[i]));
}
}
return result;
}
int main() {
#ifdef wxh010910
freopen("input.txt", "r", stdin);
#endif
int tt;
scanf("%d", &tt);
for (int ttt = 1; ttt <= tt; ++ttt) {
printf("Case #%d:", ttt);
scanf("%d %d %d", &n, &m, &w);
for (int i = 1; i <= n; ++i) {
adj[i].clear();
degree[i] = 0;
scanf("%d", &value[i]);
}
w *= 3;
inv[0] = inv[1] = 1;
for (int i = 2; i <= w; ++i) {
inv[i] = mul(md - md / i, inv[md % i]);
}
for (int i = 0; i <= w; ++i) {
power[i][0] = 1;
for (int j = 1; j <= w; ++j) {
power[i][j] = mul(power[i][j - 1], i);
}
}
while (m--) {
int x, y;
scanf("%d %d", &x, &y);
adj[x].push_back(y);
++degree[y];
}
order.clear();
for (int i = 1; i <= n; ++i) {
if (!degree[i]) {
order.push_back(i);
}
}
for (int i = 0; i < n; ++i) {
int x = order[i];
for (auto y : adj[x]) {
if (!--degree[y]) {
order.push_back(y);
}
}
}
for (int i = 0; i <= w; ++i) {
answer[i] = 0;
}
for (int i = n - 1; ~i; --i) {
int x = order[i];
for (int j = 0; j <= w; ++j) {
for (int k = 0; k < 3; ++k) {
dp[x][j][k] = power[j][value[x] * (k + 1)];
}
}
for (auto y : adj[x]) {
for (int j = 0; j <= w; ++j) {
for (int k = 0; k < 3; ++k) {
add(dp[x][j][k], dp[y][j][k]);
}
}
}
for (int j = 0; j <= w; ++j) {
int coef = mul(dp[x][j][0], mul(dp[x][j][0], dp[x][j][0]));
sub(coef, mul(3, mul(dp[x][j][0], dp[x][j][1])));
add(coef, mul(2, dp[x][j][2]));
add(answer[j], coef);
}
}
vector<int> result = interpolation(vector<int> (answer, answer + w + 1));
int inv = (md + 1) / 6;
for (int i = 1; i <= w; ++i) {
printf(" %d", mul(inv, result[i]));
}
putchar(10);
}
return 0;
}
x 2 + y 2 = k x^2+y^2=k x2+y2=k 的个数不太多,预处理后暴力即可。
#include
using namespace std;
const int N = 12345678;
const int M = 6234;
int n, m, px[N], py[N], value[M][M];
vector<pair<int, int>> all[N];
bool inside(int x, int y) {
return x >= 1 && x <= 6000 && y >= 1 && y <= 6000 && value[x][y];
}
int main() {
#ifdef wxh010910
freopen("input.txt", "r", stdin);
#endif
for (int i = 0; i <= 6000; ++i) {
for (int j = 0; j <= 6000; ++j) {
if (i * i + j * j <= 10000000) {
all[i * i + j * j].emplace_back(i, j);
} else {
break;
}
}
}
int tt;
scanf("%d", &tt);
for (int ttt = 1; ttt <= tt; ++ttt) {
printf("Case #%d:\n", ttt);
scanf("%d %d", &n, &m);
for (int i = 1; i <= n; ++i) {
scanf("%d %d", &px[i], &py[i]);
scanf("%d", &value[px[i]][py[i]]);
}
long long answer = 0;
while (m--) {
int type, x, y;
scanf("%d %d %d", &type, &x, &y);
x = (x + answer) % 6000 + 1;
y = (y + answer) % 6000 + 1;
if (type == 1) {
scanf("%d", &value[x][y]);
++n;
px[n] = x;
py[n] = y;
} else if (type == 2) {
value[x][y] = 0;
} else if (type == 3) {
int dist, weight;
scanf("%d %d", &dist, &weight);
for (auto p : all[dist]) {
if (!p.first && !p.second) {
if (inside(x + p.first, y + p.second)) {
value[x + p.first][y + p.second] += weight;
}
} else if (!p.first) {
if (inside(x + p.first, y + p.second)) {
value[x + p.first][y + p.second] += weight;
}
if (inside(x + p.first, y - p.second)) {
value[x + p.first][y - p.second] += weight;
}
} else if (!p.second) {
if (inside(x + p.first, y + p.second)) {
value[x + p.first][y + p.second] += weight;
}
if (inside(x - p.first, y + p.second)) {
value[x - p.first][y + p.second] += weight;
}
} else {
if (inside(x + p.first, y + p.second)) {
value[x + p.first][y + p.second] += weight;
}
if (inside(x + p.first, y - p.second)) {
value[x + p.first][y - p.second] += weight;
}
if (inside(x - p.first, y + p.second)) {
value[x - p.first][y + p.second] += weight;
}
if (inside(x - p.first, y - p.second)) {
value[x - p.first][y - p.second] += weight;
}
}
}
} else {
int dist;
scanf("%d", &dist);
answer = 0;
for (auto p : all[dist]) {
if (!p.first && !p.second) {
if (inside(x + p.first, y + p.second)) {
answer += value[x + p.first][y + p.second];
}
} else if (!p.first) {
if (inside(x + p.first, y + p.second)) {
answer += value[x + p.first][y + p.second];
}
if (inside(x + p.first, y - p.second)) {
answer += value[x + p.first][y - p.second];
}
} else if (!p.second) {
if (inside(x + p.first, y + p.second)) {
answer += value[x + p.first][y + p.second];
}
if (inside(x - p.first, y + p.second)) {
answer += value[x - p.first][y + p.second];
}
} else {
if (inside(x + p.first, y + p.second)) {
answer += value[x + p.first][y + p.second];
}
if (inside(x + p.first, y - p.second)) {
answer += value[x + p.first][y - p.second];
}
if (inside(x - p.first, y + p.second)) {
answer += value[x - p.first][y + p.second];
}
if (inside(x - p.first, y - p.second)) {
answer += value[x - p.first][y - p.second];
}
}
}
printf("%lld\n", answer);
}
}
for (int i = 1; i <= n; ++i) {
value[px[i]][py[i]] = 0;
}
}
return 0;
}
合法的图是这种形式的: ( 1 , 2 ) , ( 3 , 4 ) ⋯   , ( 2 n − 1 , 2 n ) (1,2), (3,4)\cdots, (2n-1,2n) (1,2),(3,4)⋯,(2n−1,2n) ,每对点对的连边之间要么 ( 2 i , 2 j ) ( 2 i + 1 , 2 j + 1 ) (2i,2j)(2i+1,2j+1) (2i,2j)(2i+1,2j+1) 要么 ( 2 i , 2 j + 1 ) ( 2 i + 1 , 2 j ) (2i,2j+1)(2i+1,2j) (2i,2j+1)(2i+1,2j) 。相当于 n n n 个点的完全图,把边黑白染色,可以将一个点的边全部取反。
oeis
用burnside统计答案,显然只用枚举每个环长,对于一组环长,答案是 2 E − V + o d d 2^{E-V+odd} 2E−V+odd , E E E 是边等价类个数, V V V 是点等价类个数, o d d odd odd 是是否存在奇环。
#include
using namespace std;
const int N = 67;
int n, md, answer, a[N], inv[N], binary[N * N], gcd[N][N];
inline void add(int &x, int y) {
x += y;
if (x >= md) {
x -= md;
}
}
inline int mul(int x, int y) {
return (long long)x * y % md;
}
void dfs(int x, int last, int remain, int same, int coef, int rank, int odd) {
if (!remain) {
add(answer, mul(coef, binary[rank + odd]));
} else {
for (a[x] = last; a[x] <= remain; ++a[x]) {
int new_coef = mul(coef, mul(inv[a[x]], a[x] == last ? inv[same + 1] : 1));
int new_rank = rank + (a[x] >> 1) - 1;
for (int i = 0; i < x; ++i) {
new_rank += gcd[a[x]][a[i]];
}
dfs(x + 1, a[x], remain - a[x], a[x] == last ? same + 1 : 1, new_coef, new_rank, odd | (a[x] & 1));
}
}
}
int main() {
#ifdef wxh010910
freopen("input.txt", "r", stdin);
#endif
int tt;
scanf("%d", &tt);
for (int ttt = 1; ttt <= tt; ++ttt) {
scanf("%d %d", &n, &md);
for (int i = 1; i <= n; ++i) {
for (int j = 1; j <= n; ++j) {
gcd[i][j] = __gcd(i, j);
}
}
inv[0] = inv[1] = 1;
for (int i = 2; i <= n; ++i) {
inv[i] = mul(md - md / i, inv[md % i]);
}
binary[0] = 1;
for (int i = 1; i <= n * n; ++i) {
binary[i] = mul(binary[i - 1], 2);
}
answer = 0;
dfs(0, 1, n, 0, 1, 0, 0);
printf("Case #%d: %d\n", ttt, answer);
}
return 0;
}
枚举 max ( ∣ a i − a j ∣ , ∣ b i − b j ∣ , ∣ c i − c j ∣ ) \max(\lvert a_i-a_j\rvert, \lvert b_i-b_j\rvert, \lvert c_i-c_j\rvert) max(∣ai−aj∣,∣bi−bj∣,∣ci−cj∣) ,然后插入,按位统计答案即可。
#include
using namespace std;
typedef unsigned long long ull;
const int N = 2345;
int a[N], b[N], c[N], f[11][2], g[11][2];
vector<int> ea[N], eb[N], ec[N];
ull value[11][2];
void merge(int *a, int *b) {
for (int i = 0; i < 11; ++i) {
f[i][0] = f[i][1] = g[i][0] = g[i][1] = value[i][0] = value[i][1] = 0;
}
for (int i = 0; i < 2048; ++i) {
if (a[i]) {
for (int j = 0; j < 11; ++j) {
f[j][i >> j & 1] += a[i];
}
}
if (b[i]) {
for (int j = 0; j < 11; ++j) {
g[j][i >> j & 1] += b[i];
}
}
}
for (int i = 0; i < 11; ++i) {
for (int j = 0; j < 2; ++j) {
for (int k = 0; k < 2; ++k) {
value[i][!(j ^ k)] += (1ull << i) * f[i][j] * g[i][k];
}
}
}
}
int main() {
#ifdef wxh010910
freopen("input.txt", "r", stdin);
#endif
int tt;
scanf("%d", &tt);
for (int ttt = 1; ttt <= tt; ++ttt) {
int a0, b0, c0, a1, b1, c1;
scanf("%d %d %d %d %d %d", &a0, &b0, &c0, &a1, &b1, &c1);
for (int i = 0; i < 2048; ++i) {
ea[i].clear();
eb[i].clear();
ec[i].clear();
a[i] = b[i] = c[i] = 0;
}
for (int i = 0; i <= a0; ++i) {
for (int j = 0; j <= a1; ++j) {
ea[abs(i - j)].push_back(i ^ j);
}
}
for (int i = 0; i <= b0; ++i) {
for (int j = 0; j <= b1; ++j) {
eb[abs(i - j)].push_back(i ^ j);
}
}
for (int i = 0; i <= c0; ++i) {
for (int j = 0; j <= c1; ++j) {
ec[abs(i - j)].push_back(i ^ j);
}
}
ull answer = 0;
for (int i = 0; i < 2048; ++i) {
if (!ea[i].empty()) {
merge(b, c);
for (auto p : ea[i]) {
++a[p];
for (int j = 0; j < 11; ++j) {
answer += value[j][(p ^ i) >> j & 1];
}
}
}
if (!eb[i].empty()) {
merge(a, c);
for (auto p : eb[i]) {
++b[p];
for (int j = 0; j < 11; ++j) {
answer += value[j][(p ^ i) >> j & 1];
}
}
}
if (!ec[i].empty()) {
merge(a, b);
for (auto p : ec[i]) {
++c[p];
for (int j = 0; j < 11; ++j) {
answer += value[j][(p ^ i) >> j & 1];
}
}
}
}
printf("Case #%d: %llu\n", ttt, answer);
}
return 0;
}
模拟。
#include
using namespace std;
int main() {
#ifdef wxh010910
freopen("input.txt", "r", stdin);
#endif
map<string, int> each;
each["char"] = each["bool"] = 1;
each["int"] = each["float"] = 4;
each["long long"] = each["double"] = 8;
each["__int128"] = each["long double"] = 16;
int tt;
cin >> tt;
for (int ttt = 1; ttt <= tt; ++ttt) {
int n;
cin >> n;
int answer = 0;
while (n--) {
string s;
cin >> s;
if (s == "long") {
string t;
cin >> t;
s = s + " " + t;
}
int type = each[s];
cin >> s;
bool inside = false;
int size = 0;
for (auto c : s) {
if (c == '[') {
inside = true;
} else if (c == ']') {
inside = false;
} else if (inside) {
size = size * 10 + c - '0';
}
}
if (!size) {
size = 1;
}
answer += type * size;
}
answer = answer + 1023 >> 10;
cout << "Case #" << ttt << ": " << answer << endl;
}
return 0;
}
考虑最后一次删除,这个人的编号是 ( k − 1 )   m o d   ( n − m + 1 ) (k-1) \bmod (n-m+1) (k−1)mod(n−m+1) ,然后倒着复原删除的过程即可。
#include
using namespace std;
int main() {
#ifdef wxh010910
freopen("input.txt", "r", stdin);
#endif
int tt;
scanf("%d", &tt);
for (int ttt = 1; ttt <= tt; ++ttt) {
long long n, m, k, answer;
scanf("%lld %lld %lld", &n, &m, &k);
if (k == 1) {
answer = m - 1;
} else {
answer = (k - 1) % (n - m + 1);
for (long long i = n - m + 2; i <= n; ) {
if (answer + k >= i) {
answer = (answer + k) % i;
++i;
} else {
long long t = min(n - i + 1, (i - answer - 2) / (k - 1));
answer += t * k;
i += t;
}
}
}
printf("Case #%d: %lld\n", ttt, answer + 1);
}
return 0;
}
答案要么是直径,要么是一对关键点之间的距离。
#include
using namespace std;
typedef double ld;
const ld pi = acos(-1);
int main() {
#ifdef wxh010910
freopen("input.txt", "r", stdin);
#endif
int tt;
scanf("%d", &tt);
for (int ttt = 1; ttt <= tt; ++ttt) {
map<ld, int> sum;
int n, radius;
scanf("%d %d", &n, &radius);
sum[0] = 0;
sum[pi * 2] = n;
while (n--) {
int x, y, r;
scanf("%d %d %d", &x, &y, &r);
if ((r + radius) * (r + radius) <= x * x + y * y || (r - radius) * (r - radius) >= x * x + y * y) {
continue;
}
ld angle = atan2(y, x);
ld delta = acos((radius * radius + x * x + y * y - r * r) / (2 * radius * sqrt(x * x + y * y)));
ld left = angle - delta;
while (left < 0) {
left += pi * 2;
}
while (left >= pi * 2) {
left -= pi * 2;
}
ld right = angle + delta;
while (right < 0) {
right += pi * 2;
}
while (right >= pi * 2) {
right -= pi * 2;
}
++sum[left];
--sum[right];
if (left > right) {
++sum[0];
}
}
int now = 0;
vector<pair<ld, ld>> segments;
for (auto p = sum.begin(); p != sum.end(); ++p) {
now += p->second;
if (!now) {
auto q = p;
++q;
segments.emplace_back(p->first, q->first);
}
}
ld answer = 0;
for (int i = 0; i < segments.size(); ++i) {
answer = max(answer, sin((segments[i].second - segments[i].first) / 2));
if (segments[i].second - segments[i].first >= pi) {
answer = 1;
}
}
for (int i = 0; i < segments.size(); ++i) {
for (int j = 0; j < i; ++j) {
answer = max(answer, sin((segments[i].first - segments[j].first) / 2));
answer = max(answer, sin((segments[i].first - segments[j].second) / 2));
answer = max(answer, sin((segments[i].second - segments[j].first) / 2));
answer = max(answer, sin((segments[i].second - segments[j].second) / 2));
ld low = max(segments[j].first, segments[i].first - pi);
ld high = min(segments[j].second, segments[i].second - pi);
if (low <= high) {
answer = 1;
}
}
}
printf("Case #%d: %.12lf\n", ttt, answer * radius * 2);
}
return 0;
}
考虑一个物品的多项式是 ( 1 + x b + x 2 b + ⋯ x a b ) (1+x^b+x^{2b}+\cdots x^{ab}) (1+xb+x2b+⋯xab) ,这个东西有逆,查询相当于查询两个前缀多项式的积的前 c c c 项和。
#include
using namespace std;
const int N = 12345;
const int M = 1234;
const int md = 1e9 + 7;
int n, m, f[N][M], g[N][M];
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 (long long)x * y % md;
}
int main() {
#ifdef wxh010910
freopen("input.txt", "r", stdin);
#endif
int tt;
scanf("%d", &tt);
for (int ttt = 1; ttt <= tt; ++ttt) {
printf("Case #%d:\n", ttt);
scanf("%d %d", &n, &m);
f[0][0] = g[0][0] = 1;
for (int i = 1; i <= n; ++i) {
int a, b;
scanf("%d %d", &a, &b);
++a;
for (int j = 0; j < b; ++j) {
for (int k = j; k <= 1000; k += b) {
f[i][k] = f[i - 1][k];
if (k >= b) {
add(f[i][k], f[i][k - b]);
}
if (k >= a * b) {
sub(f[i][k], f[i - 1][k - a * b]);
}
}
}
for (int k = 0; k <= 1000; ++k) {
g[i][k] = g[i - 1][k];
if (k >= b) {
sub(g[i][k], g[i - 1][k - b]);
}
}
if (a * b <= 1000) {
for (int j = 0; j < a * b; ++j) {
for (int k = j; k <= 1000; k += a * b) {
if (k >= a * b) {
add(g[i][k], g[i][k - a * b]);
}
}
}
}
}
int answer = 0;
while (m--) {
int l, r, c;
scanf("%d %d %d", &l, &r, &c);
l = (l + answer) % n + 1;
r = (r + answer) % n + 1;
if (l > r) {
swap(l, r);
}
--l;
int sum = 0;
for (int i = 0; i <= c; ++i) {
add(sum, f[r][i]);
}
answer = 0;
for (int i = 0; i <= c; ++i) {
add(answer, mul(sum, g[l][i]));
sub(sum, f[r][c - i]);
}
printf("%d\n", answer);
}
}
return 0;
}