Convert to Ones
翻转操作实际就是合并两段 0 0 ,特判全 的情况,假设有 k k 段 ,答案是 (k−1)min(x,y)+y ( k − 1 ) min ( x , y ) + y 。
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N = 300005;
int n, x, y;
char s[N];
int main() {
#ifdef wxh010910
freopen("input.txt", "r", stdin);
#endif
scanf("%d %d %d %s", &n, &x, &y, s);
int result = 0;
for (int i = 0; i < n; ++i) {
if (s[i] == '0') {
while (i < n && s[i] == '0') {
++i;
}
++result;
}
}
if (!result) {
puts("0");
} else {
printf("%lld\n", (ll)min(x, y) * (result - 1) + y);
}
return 0;
}
Roman Digits
原问题等价于求不同的 4a+9b+49c 4 a + 9 b + 49 c 个数,要求 a+b+c≤n a + b + c ≤ n 。注意到 n n 大于某个定值的时候答案是线性增长的,暴力较小的值就行了。
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const int answer[] = {0, 4, 10, 20, 35, 56, 83, 116, 155, 198, 244, 292};
int main() {
#ifdef wxh010910
freopen("input.txt", "r", stdin);
#endif
int n;
scanf("%d", &n);
if (n < 12) {
printf("%d\n", answer[n]);
} else {
printf("%lld\n", 49ll * (n - 11) + answer[11]);
}
return 0;
}
Sky Full of Stars
考虑计算每行每列都有至少 种颜色的方案数。如果不考虑行的要求,方案数就是 (3n−3)n ( 3 n − 3 ) n ,否则对行容斥,枚举有 i i 行只有一种颜色,如果这些行都是同色的,那么每一列都不能全是这种颜色,方案数是 ,否则方案数是 (ni)×(3i−3)3n(n−i) ( n i ) × ( 3 i − 3 ) 3 n ( n − i ) 。
answer=3n2−(3n−3)n−∑ni=1(−1)i(ni)(3(3n−i−1)n+(3i−3)3n(n−i)) a n s w e r = 3 n 2 − ( 3 n − 3 ) n − ∑ i = 1 n ( − 1 ) i ( n i ) ( 3 ( 3 n − i − 1 ) n + ( 3 i − 3 ) 3 n ( n − i ) )
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N = 1000005;
const int mod = 998244353;
int n, fac[N], ifac[N];
int add(int x, int y) {
x += y;
if (x >= mod) {
x -= mod;
}
return x;
}
int sub(int x, int y) {
x -= y;
if (x < 0) {
x += mod;
}
return x;
}
int mul(int x, int y) {
return (ll)x * y % mod;
}
int power(int x, int y) {
int result = 1;
for (; y; y >>= 1, x = mul(x, x)) {
if (y & 1) {
result = mul(result, x);
}
}
return result;
}
int binom(int x, int y) {
return mul(fac[x], mul(ifac[y], ifac[x - y]));
}
int main() {
#ifdef wxh010910
freopen("input.txt", "r", stdin);
#endif
scanf("%d", &n);
fac[0] = fac[1] = ifac[0] = ifac[1] = 1;
for (int i = 2; i <= n; ++i) {
fac[i] = mul(fac[i - 1], i);
ifac[i] = mul(mod - mod / i, ifac[mod % i]);
}
for (int i = 2; i <= n; ++i) {
ifac[i] = mul(ifac[i], ifac[i - 1]);
}
int answer = power(power(3, n), n);
answer = sub(answer, power(sub(power(3, n), 3), n));
for (int i = 1; i <= n; ++i) {
int coef = 0;
coef = add(coef, mul(3, power(sub(power(3, n - i), 1), n)));
coef = add(coef, mul(sub(power(3, i), 3), power(power(3, n - i), n)));
coef = mul(coef, binom(n, i));
if (i & 1) {
answer = add(answer, coef);
} else {
answer = sub(answer, coef);
}
}
printf("%d\n", answer);
return 0;
}
Cycles in product
设 f(i,j) f ( i , j ) 表示从第一棵树中 i i 出发走 步回到自己的方案数, g(i,j) g ( i , j ) 表示第二棵树中 i i 出发走 步回到自己的方案数,那么 answer=∑mi=0(mi)∑xf(x,i)∑yf(y,m−i) a n s w e r = ∑ i = 0 m ( m i ) ∑ x f ( x , i ) ∑ y f ( y , m − i ) 。
考虑求 f(i,j) f ( i , j ) ,枚举第一次走向哪棵子树,在子树内走了多少步转移。从下往上,从上往下分别DP一次就能求答案了。
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N = 4005;
const int M = 80;
const int mod = 998244353;
int binom[M][M];
int add(int x, int y) {
x += y;
if (x >= mod) {
x -= mod;
}
return x;
}
int sub(int x, int y) {
x -= y;
if (x < 0) {
x += mod;
}
return x;
}
int mul(int x, int y) {
return (ll)x * y % mod;
}
struct tree_t {
int n, m, dp[N][M], up[N][M], sum[N][M], down[N][M];
vector<int> adj[N];
void dpu(int x, int parent) {
for (auto y : adj[x]) {
if (y != parent) {
dpu(y, x);
for (int i = 0; i <= m; i += 2) {
sum[x][i] = add(sum[x][i], up[y][i]);
}
}
}
up[x][0] = 1;
for (int i = 2; i <= m; i += 2) {
for (int j = 0; j <= i - 2; j += 2) {
up[x][i] = add(up[x][i], mul(sum[x][j], up[x][i - j - 2]));
}
}
}
void dpd(int x, int parent) {
for (int i = 0; i <= m; i += 2) {
sum[x][i] = add(sum[x][i], down[x][i]);
}
dp[x][0] = 1;
for (int i = 2; i <= m; i += 2) {
for (int j = 0; j <= i - 2; j += 2) {
dp[x][i] = add(dp[x][i], mul(sum[x][j], dp[x][i - j - 2]));
}
}
for (auto y : adj[x]) {
if (y != parent) {
for (int i = 0; i <= m; i += 2) {
sum[x][i] = sub(sum[x][i], up[y][i]);
}
down[y][0] = 1;
for (int i = 2; i <= m; i += 2) {
for (int j = 0; j <= i - 2; j += 2) {
down[y][i] = add(down[y][i], mul(sum[x][j], down[y][i - j - 2]));
}
}
for (int i = 0; i <= m; i += 2) {
sum[x][i] = add(sum[x][i], up[y][i]);
}
dpd(y, x);
}
}
}
void init() {
for (int i = 1; i < n; ++i) {
int x, y;
scanf("%d %d", &x, &y);
adj[x].push_back(y);
adj[y].push_back(x);
}
dpu(1, 0);
dpd(1, 0);
}
} p, q;
int main() {
#ifdef wxh010910
freopen("input.txt", "r", stdin);
#endif
int m;
scanf("%d %d %d", &p.n, &q.n, &m);
p.m = q.m = m;
if (m & 1) {
puts("0");
return 0;
}
p.init();
q.init();
for (int i = 0; i <= m; ++i) {
binom[i][0] = 1;
for (int j = 1; j <= i; ++j) {
binom[i][j] = add(binom[i - 1][j], binom[i - 1][j - 1]);
}
}
int answer = 0;
for (int i = 0; i <= m; i += 2) {
int x = 0, y = 0;
for (int j = 1; j <= p.n; ++j) {
x = add(x, p.dp[j][i]);
}
for (int j = 1; j <= q.n; ++j) {
y = add(y, q.dp[j][m - i]);
}
answer = add(answer, mul(binom[m][i], mul(x, y)));
}
printf("%d\n", answer);
return 0;
}
Good Subsegments
如果不要求子区间的话,考虑 (max−min)−(r−l) ( m a x − m i n ) − ( r − l ) ,不难发现只需要用线段树维护区间最小值和最小值个数就行了。那么这里就是求区间历史最小值个数之和,线段树每个节点维护区间最小值,最小值个数,上次更新的时间,答案,以及对应的标记就可以了。
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N = 120005;
struct node_t {
int tag, time, value, number;
ll answer;
} tree[N << 2];
int n, m, top_max, top_min, a[N], stack_max[N], stack_min[N];
vector<pair<int, int>> queries[N];
ll answer[N];
void add_value(int x, int value) {
tree[x].value += value;
tree[x].tag += value;
}
void add_time(int x, int value) {
tree[x].time += value;
tree[x].answer += (ll)value * tree[x].number;
}
void push_up(int x) {
tree[x].value = min(tree[x << 1].value, tree[x << 1 | 1].value);
tree[x].answer = tree[x << 1].answer + tree[x << 1 | 1].answer;
tree[x].number = 0;
if (tree[x].value == tree[x << 1].value) {
tree[x].number += tree[x << 1].number;
}
if (tree[x].value == tree[x << 1 | 1].value) {
tree[x].number += tree[x << 1 | 1].number;
}
}
void push_down(int x) {
if (tree[x].tag) {
add_value(x << 1, tree[x].tag);
add_value(x << 1 | 1, tree[x].tag);
tree[x].tag = 0;
}
if (tree[x].time) {
if (tree[x].value == tree[x << 1].value) {
add_time(x << 1, tree[x].time);
}
if (tree[x].value == tree[x << 1 | 1].value) {
add_time(x << 1 | 1, tree[x].time);
}
tree[x].time = 0;
}
}
void build(int x, int l, int r) {
tree[x].value = l;
tree[x].number = 1;
if (l == r) {
return;
}
int mid = l + r >> 1;
build(x << 1, l, mid);
build(x << 1 | 1, mid + 1, r);
}
void modify(int x, int l, int r, int ql, int qr, int value) {
if (l == ql && r == qr) {
add_value(x, value);
return;
}
int mid = l + r >> 1;
push_down(x);
if (qr <= mid) {
modify(x << 1, l, mid, ql, qr, value);
} else if (ql > mid) {
modify(x << 1 | 1, mid + 1, r, ql, qr, value);
} else {
modify(x << 1, l, mid, ql, mid, value);
modify(x << 1 | 1, mid + 1, r, mid + 1, qr, value);
}
push_up(x);
}
ll query(int x, int l, int r, int ql, int qr) {
if (l == ql && r == qr) {
return tree[x].answer;
}
int mid = l + r >> 1;
push_down(x);
if (qr <= mid) {
return query(x << 1, l, mid, ql, qr);
} else if (ql > mid) {
return query(x << 1 | 1, mid + 1, r, ql, qr);
} else {
return query(x << 1, l, mid, ql, mid) + query(x << 1 | 1, mid + 1, r, mid + 1, qr);
}
}
int main() {
#ifdef wxh010910
freopen("input.txt", "r", stdin);
#endif
scanf("%d", &n);
for (int i = 1; i <= n; ++i) {
scanf("%d", &a[i]);
}
scanf("%d", &m);
for (int i = 1; i <= m; ++i) {
int l, r;
scanf("%d %d", &l, &r);
queries[r].push_back(make_pair(l, i));
}
build(1, 1, n);
for (int i = 1; i <= n; ++i) {
add_value(1, -1);
while (top_max && a[stack_max[top_max]] < a[i]) {
modify(1, 1, n, stack_max[top_max - 1] + 1, stack_max[top_max], a[i] - a[stack_max[top_max]]);
--top_max;
}
stack_max[++top_max] = i;
while (top_min && a[stack_min[top_min]] > a[i]) {
modify(1, 1, n, stack_min[top_min - 1] + 1, stack_min[top_min], a[stack_min[top_min]] - a[i]);
--top_min;
}
stack_min[++top_min] = i;
add_time(1, 1);
for (auto q : queries[i]) {
answer[q.second] = query(1, 1, n, q.first, i);
}
}
for (int i = 1; i <= m; ++i) {
printf("%lld\n", answer[i]);
}
return 0;
}