答案要么是 − 1 -1 −1 要么是 l c m ( n , m ) lcm(n, m) lcm(n,m) 。
#include
using namespace std;
int main() {
#ifdef wxh010910
freopen("input.txt", "r", stdin);
#endif
int n, m;
string s, t;
cin >> n >> m >> s >> t;
int gcd = __gcd(n, m);
for (int i = 0, j = 0; i < n && j < m; i += n / gcd, j += m / gcd) {
if (s[i] != t[j]) {
cout << -1 << endl;
return 0;
}
}
cout << (long long)n * m / gcd << endl;
return 0;
}
考虑 i i i 在删除 j ( i ≤ j ) j(i\le j) j(i≤j) 时产生贡献的概率,就是 j j j 在 i , i + 1 , ⋯ j i, i+1, \cdots j i,i+1,⋯j 中最早被删除的概率,即 1 j − i + 1 \frac{1}{j-i+1} j−i+11 。当 i i i 移动的时候,系数改变是 O ( 1 ) O(1) O(1) 的。
#include
using namespace std;
const int md = 1e9 + 7;
int add(int x, int y) {
x += y;
if (x >= md) {
x -= md;
}
return x;
}
int sub(int x, int y) {
x -= y;
if (x < 0) {
x += md;
}
return x;
}
int mul(int x, int y) {
return (long long)x * y % md;
}
int main() {
#ifdef wxh010910
freopen("input.txt", "r", stdin);
#endif
int n;
cin >> n;
vector<int> a(n);
for (int i = 0; i < n; ++i) {
cin >> a[i];
}
vector<int> inv(n + 1);
inv[0] = inv[1] = 1;
for (int i = 2; i <= n; ++i) {
inv[i] = mul(md - md / i, inv[md % i]);
}
int coef = 0;
for (int i = 1; i <= n; ++i) {
coef = add(coef, inv[i]);
}
int answer = 0;
for (int i = 0; i < n; ++i) {
answer = add(answer, mul(coef, a[i]));
if (i + 1 < n) {
coef = add(coef, inv[i + 2]);
coef = sub(coef, inv[n - i]);
}
}
for (int i = 1; i <= n; ++i) {
answer = mul(answer, i);
}
cout << answer << endl;
return 0;
}
首先 min ( a x , b y ) \min(a_x, b_y) min(ax,by) 可以认为是 a x + b y a_x + b_y ax+by 再减去任意一个。那么我们相当于要选出 n n n 个位置,在合法的同时使得它们的和最大。
将每个位置是否被选用 01 01 01 串表示,假设有 x x x 个 01 01 01 , y y y 个 10 10 10 , z z z 个 00 00 00 和 z z z 个 11 11 11 (显然 00 00 00 和 11 11 11 的个数是相同的),那么我们的要求是:
如果 z = 0 z = 0 z=0 ,那么要么 x = n x = n x=n ,要么 y = n y = n y=n 。
否则一定有解,先将 z z z 个 11 11 11 和 z − 1 z - 1 z−1 个 00 00 00 像这样合并成一个 11 11 11 : 11 − 00 − 11 − 00 − 11 11-00-11-00-11 11−00−11−00−11。
然后在 11 11 11 前面接上所有的 10 10 10 ,后面接上所有的 01 01 01 ,最后用剩下的 00 00 00 把它们串起来。
也就是说,特判掉 z = 0 z = 0 z=0 的情况,我们一定删的数里面一定有两个数属于同一个位置,这个贪心就可以了。
#include
using namespace std;
int main() {
#ifdef wxh010910
freopen("input.txt", "r", stdin);
#endif
int n;
cin >> n;
vector<int> a(n), b(n);
long long answer = 0, sum_a = 0, sum_b = 0;
vector<pair<int, int>> all(n << 1);
for (int i = 0; i < n; ++i) {
cin >> a[i] >> b[i];
answer += a[i] + b[i];
sum_a += a[i];
sum_b += b[i];
all[i << 1] = make_pair(a[i], i);
all[i << 1 | 1] = make_pair(b[i], i);
}
long long result = max(sum_a, sum_b);
sort(all.begin(), all.end(), greater<pair<int, int>> ());
vector<bool> visit(n);
bool already = false;
long long sum = 0;
for (int i = 0; i < n; ++i) {
if (visit[all[i].second]) {
already = true;
}
sum += all[i].first;
visit[all[i].second] = true;
}
if (already) {
result = max(result, sum);
} else {
for (int i = n; i < n << 1; ++i) {
if (all[n - 1].second == all[i].second) {
result = max(result, sum + all[i].first - all[n - 2].first);
} else {
result = max(result, sum + all[i].first - all[n - 1].first);
}
}
}
cout << answer - result << endl;
return 0;
}
记 f ( l , r ) f(l,r) f(l,r) 表示只在区间 [ l , r ] [l,r] [l,r] 内部连, l l l 和 r r r 是连通的概率,容斥即可。
#include
using namespace std;
const int md = 1e9 + 7;
int add(int x, int y) {
x += y;
if (x >= md) {
x -= md;
}
return x;
}
int sub(int x, int y) {
x -= y;
if (x < 0) {
x += md;
}
return x;
}
int mul(int x, int y) {
return (long long)x * y % md;
}
int main() {
#ifdef wxh010910
freopen("input.txt", "r", stdin);
#endif
int n, m;
cin >> n >> m;
vector<int> a(m), b(m);
for (int i = 0; i < m; ++i) {
cin >> a[i] >> b[i];
--a[i];
--b[i];
}
vector<int> ways(n + 1);
ways[0] = 1;
for (int i = 1; i <= n; ++i) {
ways[i] = mul(ways[i - 1], (i << 1) - 1);
}
vector<vector<int>> f(n << 1, vector<int> (n << 1));
vector<vector<int>> g(n << 1, vector<int> (n << 1));
int answer = 0;
for (int l = (n << 1) - 1; ~l; --l) {
for (int r = l + 1; r < n << 1; r += 2) {
bool flag = true;
int inside = 0;
for (int i = 0; i < m; ++i) {
int type = (a[i] >= l && a[i] <= r) + (b[i] >= l && b[i] <= r);
if (type == 1) {
flag = false;
break;
}
if (type) {
++inside;
}
}
if (flag) {
inside = (r - l + 1 >> 1) - inside;
int outside = n - m - inside;
f[l][r] = g[l][r] = ways[inside];
for (int i = l + 1; i < r - 1; i += 2) {
f[l][r] = sub(f[l][r], mul(f[l][i], g[i + 1][r]));
}
answer = add(answer, mul(f[l][r], ways[outside]));
}
}
}
cout << answer << endl;
return 0;
}
直接的想法是按位确定,那么需要判断一个状态是否合法。对于前缀的一个状态,需要记录当前两个序列的最大值 m a x 0 , m a x 1 max_0, max_1 max0,max1 和前缀最大值个数 c n t 0 , c n t 1 cnt_0, cnt_1 cnt0,cnt1 。对于剩下的数,我们需要安排两个序列 a 1 , a 2 , ⋯   , a x a_1, a_2, \cdots, a_x a1,a2,⋯,ax 和 b 1 , b 2 , ⋯   , b y b_1, b_2, \cdots, b_y b1,b2,⋯,by ,使得:
事实上这些条件是充要的,因为所有不是原序列前缀最大值的数,可以放在前缀最大值所在的序列后面,不产生贡献。
那么,我们可以将 a a a 和 b b b 其中一个调整成全是原序列的前缀最大值。假设 a a a 全是原序列的前缀最大值,那么我们只需要确定 b b b ,然后把剩下的没有分配的原序列前缀最大值安排到 a a a 上面就行了。
假设剩下的数里面原序列前缀最大值个数为 q q q ,有 k k k 个被放到 b b b 里面了,那么有:
c n t 0 + q − k = c n t 1 + y cnt_0 + q - k= cnt_1 + y cnt0+q−k=cnt1+y
假设 b b b 里面有 m m m 个不是原来的前缀最大值的数,那么移项变成:
2 k + m = c n t 0 − c n t 1 + q 2k + m = cnt_0 - cnt_1 + q 2k+m=cnt0−cnt1+q
注意到右边是定值,所以这个问题变成了找一个上升子序列,每个位置权值是 1 1 1 或者 2 2 2 ,要凑出某个权值。
显然如果 c c c 能凑出来,那么 c − 2 c-2 c−2 也能凑出来,所以分奇偶倒着维护一个类似最长上升子序列的DP就行了。
#include
using namespace std;
const int inf = 0x3f3f3f3f;
int main() {
#ifdef wxh010910
freopen("input.txt", "r", stdin);
#endif
int n;
cin >> n;
vector<int> a(n);
vector<bool> b(n);
int prefix = -1, q = 0;
for (int i = 0; i < n; ++i) {
cin >> a[i];
--a[i];
if (a[i] > prefix) {
prefix = a[i];
b[i] = true;
++q;
}
}
vector<int> fenw_even(n, 0), fenw_odd(n, -inf);
stack<pair<int*, int>> memory;
auto modify = [&](vector<int> &fenw, int x, int value) {
while (x < n) {
if (fenw[x] < value) {
memory.emplace(&fenw[x], fenw[x]);
fenw[x] = value;
}
x |= x + 1;
}
};
auto query = [&](vector<int> &fenw, int x) {
int result = -inf;
while (x >= 0) {
result = max(result, fenw[x]);
x = (x & x + 1) - 1;
}
return result;
};
vector<int> size(n);
for (int i = n - 1; ~i; --i) {
int even = -inf, odd = -inf;
size[i] = memory.size();
if (b[i]) {
even = query(fenw_even, n - a[i] - 1) + 2;
odd = query(fenw_odd, n - a[i] - 1) + 2;
} else {
even = query(fenw_odd, n - a[i] - 1) + 1;
odd = query(fenw_even, n - a[i] - 1) + 1;
}
if (even >= 0) {
modify(fenw_even, n - a[i] - 1, even);
}
if (odd >= 0) {
modify(fenw_odd, n - a[i] - 1, odd);
}
}
auto check = [&](int x, int need) {
if (need < 0) {
return false;
}
if (need & 1) {
return query(fenw_odd, n - x - 1) >= need;
} else {
return query(fenw_even, n - x - 1) >= need;
}
};
auto valid = [&](int max0, int max1, int diff, int q) {
return check(max0, q - diff) || check(max1, q + diff);
};
int max0 = 0, max1 = 0;
if (!check(0, q)) {
puts("-1");
return 0;
}
int diff = 0;
for (int i = 0; i < n; ++i) {
if (b[i]) {
--q;
}
while (memory.size() > size[i]) {
*memory.top().first = memory.top().second;
memory.pop();
}
if (max0 <= a[i]) {
if (valid(a[i], max1, diff + 1, q)) {
cout << 0;
max0 = a[i];
++diff;
} else {
cout << 1;
if (max1 <= a[i]) {
max1 = a[i];
--diff;
}
}
} else {
if (valid(max0, max1, diff, q)) {
cout << 0;
} else {
cout << 1;
if (max1 <= a[i]) {
max1 = a[i];
--diff;
}
}
}
}
cout << endl;
return 0;
}
假设 n ≥ m n\ge m n≥m ,考虑分治,那么只需要算从一个上面的点到下面的点的贡献。
将上方第 i i i 行第 j j j 列的数记为 U ( i , j ) U(i, j) U(i,j) ,下方的记为 D ( i , j ) D(i,j) D(i,j) ,假设上方有 H U H_U HU 行,下方有 H D H_D HD 行。为了方便这里不考虑带权。
定义:
考虑实现 M e e t i n g P o i n t MeetingPoint MeetingPoint 这个函数,那么显然有 L e f t ( i , j ) ≤ a ≤ b ≤ R i g h t ( i , j ) Left(i, j)\le a\le b\le Right(i,j) Left(i,j)≤a≤b≤Right(i,j) ,找到满足这个的使得 i i i 最小的点 ( p , q ) (p,q) (p,q) ,分为两种情况:
可以通过预处理 O ( 1 ) O(1) O(1) 查询 M e e t i n g P o i n t MeetingPoint MeetingPoint 。
再考虑实现 B o t h R e a c h a b l e BothReachable BothReachable 这个函数,对于 M e t t i n g P o i n t ( a , b ) > l MettingPoint(a,b) > l MettingPoint(a,b)>l 的情况,直接返回 0 0 0 。
其他情况,相当于数 L e f t ( y , x ) ≤ a ≤ b ≤ R i g h t ( y , x ) , y ≤ l Left(y,x)\le a\le b\le Right(y,x), y\le l Left(y,x)≤a≤b≤Right(y,x),y≤l 的 ( y , x ) (y,x) (y,x) 的个数。
如果没有 y ≤ l y\le l y≤l 这个条件,可以容斥,算:
前三个显然加上 y ≤ l y\le l y≤l 这个条件也是可以完成的,对于第四个,我们注意到这些东西的 y y y 一定比 M e t t i n g P o i n t ( a , b ) MettingPoint(a,b) MettingPoint(a,b) 小,所以可以直接忽视 y ≤ l y\le l y≤l 的条件。
在上述函数的基础上,可以定义 R e a c h a b l e ( a ) Reachable(a) Reachable(a) 表示 D ( 1 , a ) D(1,a) D(1,a) 能到达的位置个数。
枚举 y y y ,定义 L ( x ) L(x) L(x) 表示 U ( y , x ) U(y,x) U(y,x) 能到达 U ( H u , j ) U(H_u, j) U(Hu,j) 最小的 j j j , R ( x ) R(x) R(x) 表示最大的 j j j 。那么 L ( x ) , R ( x ) L(x), R(x) L(x),R(x) 都是单调不降的。问题变成:维护一个集合,支持插入 D ( 1 , j ) D(1,j) D(1,j) ,删除 D ( 1 , j ) D(1,j) D(1,j) ,询问当前集合内能到达的点个数。保证插入的 j j j 是当前最大值,删除的 j j j 是当前最小值。
定义 h a s j has_j hasj 表示 D ( 1 , j ) D(1,j) D(1,j) 能到达的,并且集合中大于 j j j 的任何位置都不能到达的位置个数。注意到删除操作是不会改变 h a s j has_j hasj 的,所以只考虑插入操作。对于 D ( 1 , j ′ ) ∈ S D(1, j')\in S D(1,j′)∈S ,如果存在 D ( 1 , j ′ ′ ) ∈ S ( j ′ ′ > j ′ ) D(1,j'')\in S(j'' > j') D(1,j′′)∈S(j′′>j′) 并且 B o t t o m ( j ′ ) ≤ B o t t o m ( j ′ ′ ) Bottom(j')\le Bottom(j'') Bottom(j′)≤Bottom(j′′) ,则 h a s j has_j hasj 不会改变。这是因为如果 j j j 和 j ′ j' j′ 都能到,那么显然 j ′ ′ j'' j′′ 也能到。所以,会改变的是一个关于 B o t t o m Bottom Bottom 的单调栈:假设是 J 1 , J 2 , ⋯   , J k J_1, J_2, \cdots, J_k J1,J2,⋯,Jk 。 h a s J k has_{J_k} hasJk 会减去 B o t h R e a c h a b l e ( J k , j , min ( B o t t o m ( J k ) , B o t t o m ( j ) ) ) BothReachable(J_k, j, \min(Bottom(J_k), Bottom(j))) BothReachable(Jk,j,min(Bottom(Jk),Bottom(j))) 。而对于 p < k p < k p<k , h a s J p has_{J_p} hasJp 会减去 D ( 1 , j ) D(1,j) D(1,j) 和 D ( 1 , J p ) D(1, J_p) D(1,Jp) 都能到,但 D ( 1 , J q ) ( q > p ) D(1, J_q)(q > p) D(1,Jq)(q>p) 不能到的格子个数。这个值是 B o t h R e a c h a b l e ( J p , j , min ( B o t t o m ( J p ) , B o t t o m ( j ) ) ) − B o t h R e a c h a b l e ( J p , j , min ( B o t t o m ( J p + 1 , j ) ) ) BothReachable(J_p, j, \min(Bottom(J_p), Bottom(j))) - BothReachable(J_p, j, \min(Bottom(J_{p+1}, j))) BothReachable(Jp,j,min(Bottom(Jp),Bottom(j)))−BothReachable(Jp,j,min(Bottom(Jp+1,j)))
。注意到当 B o t t o m ( J p ) > B o t t o m ( j ) Bottom(J_p)> Bottom(j) Bottom(Jp)>Bottom(j) 时, h a s q ( q < p ) has_q(q < p) hasq(q<p) 不变,所以可以直接用单调栈来更新。
#include
using namespace std;
const int inf = 0x3f3f3f3f;
void cmax(int &x, int y) {
if (x < y) {
x = y;
}
}
void cmin(int &x, int y) {
if (x > y) {
x = y;
}
}
int main() {
#ifdef wxh010910
freopen("input.txt", "r", stdin);
#endif
int n;
cin >> n;
vector<string> board(n);
for (int i = 0; i < n; ++i) {
cin >> board[i];
}
long long answer = 0;
function<void(vector<string>)> solve = [&](vector<string> board) {
int n = board.size(), m = board[0].size();
if (n < m) {
vector<string> rotated(m, string(n, ' '));
for (int i = 0; i < n; ++i) {
for (int j = 0; j < m; ++j) {
rotated[j][i] = board[i][j];
}
}
swap(n, m);
board = rotated;
}
if (m == 1) {
int sum = 0;
for (int i = 0; i < n; ++i) {
if (board[i][0] == '#') {
sum = 0;
} else {
answer += (board[i][0] - '0') * sum;
sum += board[i][0] - '0';
}
}
return;
}
vector<string> u = vector<string> (board.begin(), board.begin() + (n >> 1));
vector<string> d = vector<string> (board.begin() + (n >> 1), board.end());
solve(u);
solve(d);
u.push_back(d[0]);
int nu = n >> 1, nd = n + 1 >> 1;
vector<vector<int>> left(nd, vector<int> (m, inf));
vector<vector<int>> right(nd, vector<int> (m, -inf));
vector<int> top(m);
vector<int> bottom(m);
for (int i = 0; i < m; ++i) {
if (d[0][i] != '#') {
left[0][i] = right[0][i] = i;
}
}
for (int i = 0; i < nd; ++i) {
for (int j = 0; j < m; ++j) {
if (d[i][j] != '#') {
if (i) {
cmin(left[i][j], left[i - 1][j]);
cmax(right[i][j], right[i - 1][j]);
}
if (j) {
cmin(left[i][j], left[i][j - 1]);
cmax(right[i][j], right[i][j - 1]);
}
}
}
}
{
vector<vector<int>> temp(nd, vector<int> (m, -inf));
for (int i = nd - 1; ~i; --i) {
for (int j = m - 1; ~j; --j) {
if (d[i][j] != '#') {
temp[i][j] = i;
if (i + 1 < nd) {
cmax(temp[i][j], temp[i + 1][j]);
}
if (j + 1 < m) {
cmax(temp[i][j], temp[i][j + 1]);
}
}
}
}
for (int i = 0; i < m; ++i) {
bottom[i] = temp[0][i];
}
}
{
vector<vector<int>> temp(nu + 1, vector<int> (m, inf));
for (int i = 0; i <= nu; ++i) {
for (int j = 0; j < m; ++j) {
if (u[i][j] != '#') {
temp[i][j] = i;
if (i) {
cmin(temp[i][j], temp[i - 1][j]);
}
if (j) {
cmin(temp[i][j], temp[i][j - 1]);
}
}
}
}
for (int i = 0; i < m; ++i) {
top[i] = temp[nu][i];
}
}
vector<vector<int>> mp(m, vector<int> (m, inf));
for (int i = 0; i < nd; ++i) {
for (int j = 0; j < m; ++j) {
if (left[i][j] <= right[i][j]) {
cmin(mp[left[i][j]][right[i][j]], i);
}
}
}
for (int l = 0; l < m; ++l) {
for (int r = m - 1; ~r; --r) {
if (l) {
cmin(mp[l][r], mp[l - 1][r]);
}
if (r + 1 < m) {
cmin(mp[l][r], mp[l][r + 1]);
}
}
}
auto meeting_point = [&](int a, int b) {
int p = mp[a][b];
return p <= min(bottom[a], bottom[b]) ? p : inf;
};
vector<int> sum1(nd);
vector<vector<int>> sum2(nd, vector<int> (m));
vector<vector<int>> sum3(nd, vector<int> (m));
vector<vector<int>> sum4(m, vector<int> (m));
for (int i = 0; i < nd; ++i) {
if (i) {
sum1[i] = sum1[i - 1];
for (int j = 0; j < m; ++j) {
sum2[i][j] = sum2[i - 1][j];
sum3[i][j] = sum3[i - 1][j];
}
}
for (int j = 0; j < m; ++j) {
if (left[i][j] <= right[i][j]) {
sum1[i] += d[i][j] - '0';
if (left[i][j]) {
sum2[i][left[i][j] - 1] += d[i][j] - '0';
}
if (right[i][j] + 1 < m) {
sum3[i][right[i][j] + 1] += d[i][j] - '0';
}
if (left[i][j] && right[i][j] + 1 < m) {
sum4[left[i][j] - 1][right[i][j] + 1] += d[i][j] - '0';
}
}
}
}
for (int i = 0; i < nd; ++i) {
for (int j = m - 1; j; --j) {
sum2[i][j - 1] += sum2[i][j];
}
for (int j = 1; j < m; ++j) {
sum3[i][j] += sum3[i][j - 1];
}
}
for (int l = m - 1; ~l; --l) {
for (int r = l; r < m; ++r) {
if (l + 1 < m) {
sum4[l][r] += sum4[l + 1][r];
}
if (r) {
sum4[l][r] += sum4[l][r - 1];
}
if (l + 1 < m && r) {
sum4[l][r] -= sum4[l + 1][r - 1];
}
}
}
auto both_reachable = [&](int a, int b, int l) {
if (meeting_point(a, b) > l) {
return 0;
} else {
return sum1[l] - sum2[l][a] - sum3[l][b] + sum4[a][b];
}
};
vector<int> reachable(m);
for (int i = 0; i < m; ++i) {
reachable[i] = both_reachable(i, i, bottom[i]);
}
vector<vector<int>> event(nu);
vector<bool> ban(m);
for (int i = 0; i < m; ++i) {
if (top[i] >= nu) {
ban[i] = true;
} else {
event[top[i]].push_back(i);
}
}
vector<int> l(m), r(m);
for (int i = m - 1; ~i; --i) {
if (d[0][i] == '#') {
l[i] = inf;
r[i] = -inf;
} else {
l[i] = i;
r[i] = max(i, i + 1 < m ? r[i + 1] : -inf);
}
}
for (int row = nu - 1; ~row; --row) {
for (int i = m - 1; ~i; --i) {
if (u[row][i] == '#') {
l[i] = inf;
r[i] = -inf;
} else if (i + 1 < m) {
cmin(l[i], l[i + 1]);
cmax(r[i], r[i + 1]);
}
}
vector<int> has(m);
vector<int> st(m);
int sum = 0, stl = 0, str = 0, myl = 0, myr = -1;
auto insert = [&](int x) {
if (!ban[x]) {
if (stl < str) {
sum -= has[st[str - 1]];
has[st[str - 1]] -= both_reachable(st[str - 1], x, min(bottom[st[str - 1]], bottom[x]));
sum += has[st[str - 1]];
if (bottom[st[str - 1]] <= bottom[x]) {
--str;
while (stl < str) {
sum -= has[st[str - 1]];
has[st[str - 1]] -= both_reachable(st[str - 1], x, min(bottom[st[str - 1]], bottom[x])) - both_reachable(st[str - 1], x, min(bottom[st[str]], bottom[x]));
sum += has[st[str - 1]];
if (bottom[st[str - 1]] <= bottom[x]) {
--str;
} else {
break;
}
}
}
}
st[str++] = x;
has[x] = reachable[x];
sum += has[x];
}
};
auto erase = [&](int x) {
if (!ban[x]) {
sum -= has[x];
if (stl < str && st[stl] == x) {
++stl;
}
}
};
for (int i = 0; i < m; ++i) {
if (l[i] <= r[i]) {
while (myr < r[i]) {
insert(++myr);
}
while (myl < l[i]) {
erase(myl++);
}
answer += (u[row][i] - '0') * sum;
}
}
for (auto p : event[row]) {
ban[p] = true;
}
}
};
solve(board);
cout << answer << endl;
return 0;
}