10^N+7
模拟。
#include <bits/stdc++.h>
using namespace std;
int main() {
#ifdef wxh010910
freopen("input.txt", "r", stdin);
#endif
int x, y, z;
scanf("%d %d %d", &x, &y, &z);
long long answer = z;
while (true) {
if (answer % 17 == x && answer % 107 == y) {
printf("%lld\n", answer);
return 0;
}
answer += 1000000007;
}
return 0;
}
Coins
将相邻两个的面值作商,然后做个背包。
#include <bits/stdc++.h>
using namespace std;
int main() {
#ifdef wxh010910
freopen("input.txt", "r", stdin);
#endif
long long n;
scanf("%lld", &n);
vector<int> a = {5, 2, 5, 2, 5};
function<int(int, long long)> solve = [&](int x, long long n) {
if (x == 5) {
return 1;
} else {
int answer = 0;
for (int i = 0; i < a[x] && i <= n; ++i) {
answer += solve(x + 1, n - i);
}
return answer;
}
};
printf("%d\n", solve(0, n));
return 0;
}
Equiangular
记 x i x_i xi 表示第 i i i 条边经过了多少个点,不难发现 x i + x i + 1 x_i + x_{i+1} xi+xi+1 是定值,枚举约数算算就好了。
#include <bits/stdc++.h>
using namespace std;
int main() {
#ifdef wxh010910
freopen("input.txt", "r", stdin);
#endif
long long n;
scanf("%lld", &n);
long long answer = 0;
auto solve = [&](long long x) {
answer += x == n ? 0 : x + 1 >> 1;
};
for (int i = 1; (long long)i * i <= n; ++i) {
if (n % i == 0) {
solve(i);
if ((long long)i * i != n) {
solve(n / i);
}
}
}
if (!(n & 1)) {
--answer;
}
printf("%lld\n", answer);
return 0;
}
Knapsack And Queries
用两个栈维护背包,如果删除的时候某一边不够了将另一边取一半过来暴力重构,而之后再重构需要至少 s i z e 2 \frac{size}{2} 2size 的代价,所以可以认为均摊地认为复杂度是线性的。
#include <bits/stdc++.h>
using namespace std;
class crypto_t {
public:
crypto_t() {
sm = cnt = 0;
seed();
}
int decode(int z) {
z ^= next();
z ^= (next() << 8);
z ^= (next() << 16);
z ^= (next() << 22);
return z;
}
void query(long long z) {
const long long B = 425481007;
const long long MD = 1000000007;
cnt++;
sm = ((sm * B % MD + z) % MD + MD) % MD;
seed();
}
private:
long long sm;
int cnt;
uint8_t data[256];
int I, J;
void swap_data(int i, int j) {
uint8_t tmp = data[i];
data[i] = data[j];
data[j] = tmp;
}
void seed() {
uint8_t key[8];
for (int i = 0; i < 4; i++) {
key[i] = (sm >> (i * 8));
}
for (int i = 0; i < 4; i++) {
key[i + 4] = (cnt >> (i * 8));
}
for (int i = 0; i < 256; i++) {
data[i] = i;
}
I = J = 0;
int j = 0;
for (int i = 0; i < 256; i++) {
j = (j + data[i] + key[i % 8]) % 256;
swap_data(i, j);
}
}
uint8_t next() {
I = (I + 1) % 256;
J = (J + data[I]) % 256;
swap_data(I, J);
return data[(data[I] + data[J]) % 256];
}
};
typedef long long ll;
const ll inf = 1ll << 60;
int main() {
#ifdef wxh010910
freopen("input.txt", "r", stdin);
#endif
int n, m;
scanf("%d %d", &m, &n);
struct stack_t {
vector<vector<ll>> stack;
int m;
stack_t(int m):m(m) {
clear();
}
void clear() {
stack.clear();
stack.push_back(vector<ll> (m, -inf));
stack[0][0] = 0;
}
void pop() {
stack.pop_back();
}
void push(int weight, int value) {
vector<ll> f = stack.back();
for (int i = 0; i < weight; ++i) {
f[i] = max(f[i], stack.back()[m + i - weight] + value);
}
for (int i = weight; i < m; ++i) {
f[i] = max(f[i], stack.back()[i - weight] + value);
}
stack.push_back(f);
}
};
stack_t left(m), right(m);
crypto_t c;
vector<pair<int, int>> vleft, vright;
auto query = [&](int l, int r) {
ll answer = -inf;
eque<int> q;
for (int i = r - 1; i > l; --i) {
while (!q.empty() && left.stack.back()[q.back()] <= left.stack.back()[i]) {
q.pop_back();
}
q.push_back(i);
}
for (int i = 0; i < m; ++i) {
while (!q.empty()) {
if (l < r) {
if (q.front() < l || q.front() >= r) {
q.pop_front();
} else {
break;
}
} else {
if (q.front() < l && q.front() >= r) {
q.pop_front();
} else {
break;
}
}
}
while (!q.empty() && left.stack.back()[q.back()] <= left.stack.back()[l]) {
q.pop_back();
}
q.push_back(l);
answer = max(answer, left.stack.back()[q.front()] + right.stack.back()[i]);
if (l) {
--l;
} else {
l = m - 1;
}
if (r) {
--r;
} else {
r = m - 1;
}
}
return answer;
};
for (int i = 0; i < n; i++) {
int t, w, v, l, r;
scanf("%d %d %d %d %d", &t, &w, &v, &l, &r);
t = c.decode(t);
w = c.decode(w);
v = c.decode(v);
l = c.decode(l);
r = c.decode(r);
if (t == 1) {
right.push(w % m, v);
vright.emplace_back(w % m, v);
} else {
if (vleft.empty()) {
left.clear();
right.clear();
int mid = vright.size() >> 1;
vector<pair<int, int>> temp = vright;
vright.clear();
for (int i = mid; i; --i) {
vleft.push_back(temp[i]);
left.push(temp[i].first, temp[i].second);
}
for (int i = mid + 1; i < temp.size(); ++i) {
vright.push_back(temp[i]);
right.push(temp[i].first, temp[i].second);
}
} else {
left.pop();
vleft.pop_back();
}
}
long long answer = query(l, r + 1);
if (answer < 0) {
answer = -1;
}
c.query(answer);
printf("%lld\n", answer);
}
}
Self-contained
可以证明答案只有两种形式:
- 0 , d , 2 d , 3 d , ⋯   , k d 0, d, 2d, 3d, \cdots, kd 0,d,2d,3d,⋯,kd
- 0 , a , b , a + b 0, a, b, a+b 0,a,b,a+b
第一种可以暴力算,第二种求个卷积就行了。
证明:
显然 0 0 0 必须在集合中,记最大的数为 n n n 。
对于 1 ≤ a < n , a ∈ S 1\le a < n, a\in S 1≤a<n,a∈S ,因为 a + n > n a + n > n a+n>n 所以 n − a ∈ S n-a\in S n−a∈S 。
不妨假设 n 2 \frac{n}{2} 2n 不在集合中,那么 S = { 0 , a 1 , a 2 , ⋯   , a k , n − a k , n − a k − 1 , ⋯   , n − a 1 , n } S = \{0, a_1, a_2, \cdots, a_k, n - a_k, n-a_{k-1}, \cdots, n - a_1, n\} S={0,a1,a2,⋯,ak,n−ak,n−ak−1,⋯,n−a1,n} 。
当 k ≥ 2 k\ge 2 k≥2 时 ∀ 1 ≤ i < j ≤ k \forall 1\le i < j\le k ∀1≤i<j≤k 有 ( n − a i ) + ( n − a j ) > n (n - a_i) + (n - a_j) > n (n−ai)+(n−aj)>n 所以 a j − a i ∈ S a_j - a_i\in S aj−ai∈S 。
又因为 a j − a i < a j ≤ a k a_j - a_i < a_j\le a_k aj−ai<aj≤ak 所以 a j − a i ≤ { a 1 , a 2 , ⋯   , a k − 1 } a_j - a_i\le \{a_1, a_2, \cdots, a_{k-1}\} aj−ai≤{a1,a2,⋯,ak−1} 。
那么 a 2 − a 1 < a 3 − a 1 < a 4 − a 1 < ⋯ < a k − a 1 ∈ { a 1 , a 2 , ⋯   , a k − 1 } a_2 - a_1 < a_3 - a_1 < a_4 - a_1 < \cdots < a_k - a_1 \in \{a_1, a_2, \cdots, a_{k-1}\} a2−a1<a3−a1<a4−a1<⋯<ak−a1∈{a1,a2,⋯,ak−1} 。
只能有 a 2 − a 1 = a 3 − a 2 = a 4 − a 3 = ⋯ = a k − a k − 1 = a 1 a_2 - a_1 = a_3 - a_2 = a_4 - a_3 = \cdots = a_k - a_{k-1} = a_1 a2−a1=a3−a2=a4−a3=⋯=ak−ak−1=a1 。
又因为 n − a 1 − a k ∈ S n - a_1 - a_k\in S n−a1−ak∈S 可以推出 n = ( 2 k + 1 ) a 1 n = (2k + 1)a_1 n=(2k+1)a1 。
另一种情况类似处理。
#include <bits/stdc++.h>
using namespace std;
const int md = 998244353;
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 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;
}
namespace ntt {
int base = 1, root = -1, max_base = -1;
vector<int> roots = {0, 1}, rev = {0, 1};
void init() {
int temp = md - 1;
max_base = 0;
while (!(temp & 1)) {
temp >>= 1;
++max_base;
}
root = 2;
while (true) {
if (power(root, 1 << max_base) == 1 && power(root, 1 << max_base - 1) != 1) {
break;
}
++root;
}
}
void ensure_base(int nbase) {
if (max_base == -1) {
init();
}
if (nbase <= base) {
return;
}
assert(nbase <= max_base);
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) {
int z = power(root, 1 << max_base - 1 - base);
for (int i = 1 << base - 1; i < 1 << base; ++i) {
roots[i << 1] = roots[i];
roots[i << 1 | 1] = mul(roots[i], z);
}
++base;
}
}
void dft(vector<int> &a) {
int n = a.size(), 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) {
int x = a[j + k], y = mul(a[j + k + i], roots[i + k]);
a[j + k] = add(x, y);
a[j + k + i] = sub(x, y);
}
}
}
}
vector<int> pmul(vector<int> a, vector<int> b, bool equal = false) {
int need = a.size() + b.size() - 1, nbase = 0;
while (1 << nbase < need) {
++nbase;
}
ensure_base(nbase);
int size = 1 << nbase;
a.resize(size);
b.resize(size);
dft(a);
if (equal) {
b = a;
} else {
dft(b);
}
int inv = power(size, md - 2);
for (int i = 0; i < size; ++i) {
a[i] = mul(mul(a[i], b[i]), inv);
}
reverse(a.begin() + 1, a.end());
dft(a);
a.resize(need);
return a;
}
vector<int> psqr(vector<int> a) {
return pmul(a, a, true);
}
}
using ntt::psqr;
int main() {
#ifdef wxh010910
freopen("input.txt", "r", stdin);
#endif
string s;
cin >> s;
int n = s.length();
vector<int> a(n);
for (int i = 0; i < n; ++i) {
a[i] = s[i] - '0';
}
if (!a[0]) {
puts("0");
return 0;
}
int answer = 1;
vector<int> c = psqr(a);
for (int i = 3; i < n; ++i) {
if (a[i]) {
int temp = c[i] - 2;
if (!(i & 1) && a[i >> 1]) {
--temp;
}
if (temp) {
answer = 4;
}
}
}
for (int d = 1; d < n; ++d) {
int result = 0;
for (int i = 0; i < n; i += d) {
if (!a[i]) {
break;
}
++result;
}
answer = max(answer, result);
}
printf("%d\n", answer);
return 0;
}
Point Sequences
在模意义下做。
#include <bits/stdc++.h>
using namespace std;
const int md = 1004535809;
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 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;
}
struct point_t {
int x, y;
point_t(int x = 0, int y = 0):x(x), y(y) {
}
point_t operator + (const point_t &b) const {
return point_t(add(x, b.x), add(y, b.y));
}
point_t operator - (const point_t &b) const {
return point_t(sub(x, b.x), sub(y, b.y));
}
point_t operator * (const int &b) const {
return point_t(mul(x, b), mul(y, b));
}
void read() {
scanf("%d %d", &x, &y);
if (x < 0) {
x += md;
}
if (y < 0) {
y += md;
}
}
};
int cross(point_t a, point_t b) {
return sub(mul(a.x, b.y), mul(b.x, a.y));
}
int main() {
#ifdef wxh010910
freopen("input.txt", "r", stdin);
#endif
int n;
scanf("%d", &n);
vector<point_t> a(n), b(n), c(n), d(n + 1);
d[0].read();
for (int i = 0; i < n; ++i) {
a[i].read();
b[i].read();
c[i].read();
}
for (int i = 0; i < n; ++i) {
int x = cross(a[i] - c[i], d[i] - c[i]);
int y = cross(d[i] - c[i], b[i] - c[i]);
int temp = add(x, y);
if (!temp) {
printf("%d\n", i);
return 0;
}
temp = power(temp, md - 2);
d[i + 1] = (a[i] * y + b[i] * x) * temp;
}
printf("%d\n", n);
return 0;
}
Construct One Point
用Pick定理可以判断一个三角形内部是否有点,考虑最长边 P Q PQ PQ ,如果最长边上有整点,找到最接近中点那个点 S S S ,如果 R S RS RS 上有整点就做完了,否则可以递归求。接下来为了方便可以认为 P = ( 0 , 0 ) , Q = ( x , y ) P = (0, 0), Q = (x, y) P=(0,0),Q=(x,y) ,且 gcd ( x , y ) = 1 \gcd(x, y) = 1 gcd(x,y)=1 ,考虑求出一个点 ( u , v ) (u, v) (u,v) 满足 u y − v x = − 1 ( 0 ≤ u < x , 0 ≤ v < y ) uy - vx = -1(0\le u < x, 0\le v < y) uy−vx=−1(0≤u<x,0≤v<y) ,那么 ( u , v ) (u, v) (u,v) 一定是一组解。
证明:
首先可以证明 gcd ( x − u , y − v ) = 1 \gcd(x-u, y-v) = 1 gcd(x−u,y−v)=1 ,并且 ( 0 , 0 ) ( u , v ) ( x , y ) (0, 0) (u, v) (x,y) (0,0)(u,v)(x,y) 这个三角形内是没有整点的(因为面积为 1 2 \frac{1}{2} 21 ,用Pick定理证就行了),然后 ( 0 , 0 ) ( x , y ) ( 2 u − x , 2 v − y ) (0, 0)(x,y)(2u - x, 2v - y) (0,0)(x,y)(2u−x,2v−y) 内部也没有整点:边界多了 1 1 1 ,面积多了 1 2 \frac{1}{2} 21 ,然后一直延伸下去也没有整点。过 ( 0 , 0 ) (0, 0) (0,0) 作一条与 ( u , v ) ( x , y ) (u, v) (x,y) (u,v)(x,y) 两点形成直线平行的线,然后就相当于一个带形区域没有整点,因为 P Q PQ PQ 是最长边,而 R R R 要离开带形区域的话就会让 P Q PQ PQ 的一个邻角变成钝角,矛盾。对 ( 0 , 0 ) , ( u , v ) (0,0), (u,v) (0,0),(u,v) 也类似考虑一下,就可以证明完结论了(可能需要画图解释比较清楚)。
#include <bits/stdc++.h>
using namespace std;
struct point_t {
int x, y;
point_t(int x = 0, int 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);
}
point_t operator * (const int &b) const {
return point_t(x * b, y * b);
}
point_t operator / (const int &b) const {
return point_t(x / b, y / b);
}
long long operator * (const point_t &b) const {
return (long long)x * b.y - (long long)y * b.x;
}
};
long long dist(point_t u, point_t v) {
return (long long)(u.x - v.x) * (u.x - v.x) + (long long)(u.y - v.y) * (u.y - v.y);
}
int gcd(point_t u, point_t v) {
return __gcd(abs(u.x - v.x), abs(u.y - v.y));
}
long long area(point_t u, point_t v, point_t w) {
return (v - u) * (w - u);
}
bool check(point_t u, point_t v, point_t w) {
return area(u, v, w) > gcd(u, v) + gcd(v, w) + gcd(w, u) - 2;
}
void exgcd(long long x, long long y, long long &a, long long &b) {
if (!y) {
a = 1;
b = 0;
} else {
exgcd(y, x % y, b, a);
b -= x / y * a;
}
}
int inv(int x, int y) {
long long a, b;
exgcd(x, y, a, b);
return (a % y + y) % y;
}
void solve(point_t u, point_t v, point_t w) {
if (dist(u, v) < dist(v, w)) {
solve(v, w, u);
} else if (dist(u, v) < dist(w, u)) {
solve(w, u, v);
} else if (gcd(u, v) > 1) {
int g = gcd(u, v);
point_t p = u + (v - u) / g * (g >> 1);
if (gcd(p, w) > 1) {
p = p + (w - p) / gcd(p, w);
printf("%d %d\n", p.x, p.y);
} else if (check(u, p, w)) {
solve(u, p, w);
} else {
solve(p, v, w);
}
} else {
bool swapped = false;
if (u.x > v.x) {
swapped = true;
swap(u, v);
}
int x = v.x - u.x, y = abs(v.y - u.y), dy = y > 1 ? inv(x % y, y) : 1, dx = ((long long)dy * x - 1) / y;
point_t p = v.y > u.y ? point_t(u.x + dx, u.y + dy) : point_t(v.x - dx, v.y + dy);
if (swapped) {
p = point_t(u.x + v.x - p.x, u.y + v.y - p.y);
}
printf("%d %d\n", p.x, p.y);
}
}
int main() {
#ifdef wxh010910
freopen("input.txt", "r", stdin);
#endif
int n;
scanf("%d", &n);
while (n--) {
point_t p, q, r;
scanf("%d %d %d %d %d %d", &p.x, &p.y, &q.x, &q.y, &r.x, &r.y);
if (check(p, q, r)) {
solve(p, q, r);
} else {
puts("-1 -1");
}
}
return 0;
}
Prefix Suffix Free
考虑用总的减去不合法的,对于一个前缀,如果有一个非空border则会在之前算答案,所以只需要找出不存在非空border的所有长度即可。
#include <bits/stdc++.h>
using namespace std;
const int md = 1e9 + 7;
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
string s;
cin >> s;
int n = s.length();
vector<int> power(n + 1);
power[0] = 1;
for (int i = 1; i <= n; ++i) {
power[i] = mul(power[i - 1], 26);
}
vector<int> fail(n);
fail[0] = -1;
for (int i = 1; i < n; ++i) {
fail[i] = fail[i - 1];
while (~fail[i] && s[fail[i] + 1] != s[i]) {
fail[i] = fail[fail[i]];
}
if (s[fail[i] + 1] == s[i]) {
++fail[i];
}
}
int answer = power[n];
for (int i = 0; i < n; ++i) {
if (!~fail[i]) {
answer = sub(answer, power[n - i - 1]);
}
}
printf("%d\n", answer);
return 0;
}
ADD DIV MAX RESTORE
标记类似于 ( x + a ) / b + c (x + a) / b + c (x+a)/b+c 的形式,不难发现标记是可以合并的。需要注意的是当 b b b 过大时,只会出现两种值,然后用线段树维护就好了。
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const int inf = 300000000;
struct operation_t {
bool init, type;
int x, y, z;
operation_t(bool init = false, bool type = false, int x = 0, int y = 0, int z = 1):init(init), type(type), x(x), y(y), z(z) {
}
};
class segtree_t {
private:
vector<pair<int, int>> tree;
vector<operation_t> tag;
int n;
int apply(int value, operation_t operation) {
if (operation.type) {
return value < operation.z ? operation.x : operation.y;
} else {
return (value + operation.x) / operation.z + operation.y;
}
}
operation_t unite(operation_t l, operation_t r) {
if (r.init) {
return r;
} else if (l.type) {
return operation_t(l.init, true, apply(l.x, r), apply(l.y, r), l.z);
} else if (r.type) {
return operation_t(l.init, true, r.x, r.y, min((ll)inf + 1, max((ll)max(r.z - l.y, 0) * l.z - l.x, 0ll)));
} else {
ll mul = (ll)l.z * r.z, add = (ll)(l.y + r.x) * l.z + l.x;
if (mul <= inf) {
return operation_t(l.init, false, add % mul, add / mul + r.y, mul);
} else {
return operation_t(l.init, true, add / mul + r.y, add / mul + r.y + 1, min((ll)inf + 1, mul - add % mul));
}
}
}
void add(int x, operation_t operation) {
if (operation.init) {
tree[x].first = apply(tree[x].second, operation);
} else {
tree[x].first = apply(tree[x].first, operation);
}
tag[x] = unite(tag[x], operation);
}
void push_up(int x, int z) {
tree[x].first = max(tree[x + 1].first, tree[z].first);
tree[x].second = max(tree[x + 1].second, tree[z].second);
}
void push_down(int x, int z) {
add(x + 1, tag[x]);
add(z, tag[x]);
tag[x] = operation_t();
}
void build(int x, int l, int r, vector<int> &a) {
if (l == r) {
tree[x] = make_pair(a[l], a[l]);
} else {
int y = l + r >> 1, z = x + (y - l + 1 << 1);
build(x + 1, l, y, a);
build(z, y + 1, r, a);
push_up(x, z);
}
}
void modify(int x, int l, int r, int ql, int qr, operation_t operation) {
if (l == ql && r == qr) {
add(x, operation);
} else {
int y = l + r >> 1, z = x + (y - l + 1 << 1);
push_down(x, z);
if (qr <= y) {
modify(x + 1, l, y, ql, qr, operation);
} else if (ql > y) {
modify(z, y + 1, r, ql, qr, operation);
} else {
modify(x + 1, l, y, ql, y, operation);
modify(z, y + 1, r, y + 1, qr, operation);
}
push_up(x, z);
}
}
int query(int x, int l, int r, int ql, int qr) {
if (l == ql && r == qr) {
return tree[x].first;
} else {
int y = l + r >> 1, z = x + (y - l + 1 << 1);
push_down(x, z);
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 max(query(x + 1, l, y, ql, y), query(z, y + 1, r, y + 1, qr));
}
}
}
public:
segtree_t(vector<int> a) {
n = a.size();
tree.resize((n << 1) - 1);
tag.resize((n << 1) - 1);
build(0, 0, n - 1, a);
}
void modify(int l, int r, operation_t operation) {
modify(0, 0, n - 1, l, r, operation);
}
int query(int l, int r) {
return query(0, 0, n - 1, l, r);
}
};
int main() {
#ifdef wxh010910
freopen("input.txt", "r", stdin);
#endif
int n, m;
scanf("%d %d", &n, &m);
vector<int> a(n);
for (int i = 0; i < n; ++i) {
scanf("%d", &a[i]);
}
segtree_t segtree(a);
while (m--) {
int type, l, r, x;
scanf("%d %d %d %d", &type, &l, &r, &x);
if (!type) {
segtree.modify(l, r, operation_t(false, false, 0, x, 1));
} else if (type == 1) {
segtree.modify(l, r, operation_t(false, false, 0, 0, x));
} else if (type == 2) {
printf("%d\n", segtree.query(l, r));
} else {
segtree.modify(l, r, operation_t(true, false, 0, 0, 1));
}
}
return 0;
}
AB Sort
将 A A A 看成 + 1 +1 +1 ,将 B B B 看成 − 1 -1 −1 ,画成一条折线,那么一次操作会将最低的谷的高度提高 1 1 1 ,所以答案就是 A A A 的个数减去最小前缀和加 1 1 1 ,线段树维护即可。
#include <bits/stdc++.h>
using namespace std;
struct node_t {
int a, b, ab, ba;
bool rev;
void apply() {
swap(a, b);
swap(ab, ba);
rev = !rev;
}
};
class segtree_t {
private:
vector<node_t> tree;
int n;
void push_up(int x, int z) {
tree[x].a = tree[x + 1].a + tree[z].a;
tree[x].b = tree[x + 1].b + tree[z].b;
tree[x].ab = max(tree[x + 1].ab + tree[z].a, tree[x + 1].b + tree[z].ab);
tree[x].ba = max(tree[x + 1].ba + tree[z].b, tree[x + 1].a + tree[z].ba);
}
void push_down(int x, int z) {
if (tree[x].rev) {
tree[x + 1].apply();
tree[z].apply();
tree[x].rev = !tree[x].rev;
}
}
void build(int x, int l, int r, vector<int> &a) {
if (l == r) {
if (a[l]) {
tree[x].b = 1;
} else {
tree[x].a = 1;
}
tree[x].ab = tree[x].ba = 1;
} else {
int y = l + r >> 1, z = x + (y - l + 1 << 1);
build(x + 1, l, y, a);
build(z, y + 1, r, a);
push_up(x, z);
}
}
void modify(int x, int l, int r, int ql, int qr) {
if (l == ql && r == qr) {
tree[x].apply();
} else {
int y = l + r >> 1, z = x + (y - l + 1 << 1);
push_down(x, z);
if (qr <= y) {
modify(x + 1, l, y, ql, qr);
} else if (ql > y) {
modify(z, y + 1, r, ql, qr);
} else {
modify(x + 1, l, y, ql, y);
modify(z, y + 1, r, y + 1, qr);
}
push_up(x, z);
}
}
public:
segtree_t(int n):n(n) {
tree.resize((n << 1) - 1);
}
void build(vector<int> a) {
build(0, 0, n - 1, a);
}
void modify(int l, int r) {
modify(0, 0, n - 1, l, r);
}
int query() {
return tree[0].ab + 1;
}
};
int main() {
#ifdef wxh010910
freopen("input.txt", "r", stdin);
#endif
ios::sync_with_stdio(0);
cin.tie(0);
int n;
string s;
cin >> n >> s;
segtree_t segtree(n);
vector<int> a(n);
for (int i = 0; i < n; ++i) {
a[i] = s[i] - 'A';
}
segtree.build(a);
int q;
cin >> q;
while (q--) {
int l, r;
cin >> l >> r;
segtree.modify(l, r);
printf("%d\n", segtree.query());
}
return 0;
}
Short LIS
特判掉 a + b = n − 1 a + b = n - 1 a+b=n−1 的情况。
当 a + b < n − 1 a + b < n - 1 a+b<n−1 时,可以将序列和值域都反转, a ′ = n − a − 1 , b ′ = n − b − 1 a' = n - a - 1, b' = n - b - 1 a′=n−a−1,b′=n−b−1 。
将 ( i , p i ) (i, p_i) (i,pi) 在平面上画出来, ( a , b ) (a, b) (a,b) 将平面分成了四个区域,不难发现左下和右上不能同时有点。
假设左下没有点,那么左上有 a a a 个点,右下有 b b b 个点,右上有 n − 1 − a − b < 0 n - 1 - a - b < 0 n−1−a−b<0 个点,不合法。
不难发现左下的点一定是单调递减的,可以将问题划分成下方和左方两个子问题,相当于要解决 b = n − 1 b = n - 1 b=n−1 的情况。
一个合法的序列可以唯一对应一条从 ( 0 , n ) (0, n) (0,n) 到 ( n , 0 ) (n, 0) (n,0) 的路径,满足任意时刻 x + y ≤ n x + y\le n x+y≤n ,路径长成 f ( x ) = min i ≤ x p i f(x) = \min_{i\le x} p_i f(x)=mini≤xpi 的形式。
那么 b = n − 1 b = n-1 b=n−1 的情况相当于一个长度为 b b b 的合法序列,前 a a a 个位置都是前缀最小值。
前 a a a 个位置假设往下走了 t ( t ≥ a ) t(t\ge a) t(t≥a) 步,
前面就是方程 x 1 + x 2 + x 3 + ⋯ + x a = t x_1 + x_2 + x_3 + \cdots + x_a = t x1+x2+x3+⋯+xa=t 的正整数解的个数,就是 ( t − 1 a − 1 ) \binom{t-1}{a-1} (a−1t−1) 。
后面是从 ( a , b − t ) (a, b-t) (a,b−t) 走到 ( b , 0 ) (b, 0) (b,0) 的方案数,是 ( 2 b − a − t b − a ) − ( 2 b − a − t b − a + 1 ) \binom{2b - a - t}{b - a} - \binom{2b - a - t}{b - a + 1} (b−a2b−a−t)−(b−a+12b−a−t) 。
∑ t ( t − 1 a − 1 ) ( 2 b − a − t b − a ) = ( 2 b − a b ) \sum_t \binom{t-1}{a-1} \binom{2b-a-t}{b-a} = \binom{2b-a}{b} ∑t(a−1t−1)(b−a2b−a−t)=(b2b−a) 。
组合解释是相当于从 2 b − a 2b-a 2b−a 个数里面选 b b b 个,以第 a a a 个数为分界线,枚举左右各有多少个数。
同理 ∑ t ( t − 1 a − 1 ) ( 2 b − a − t b − a + 1 ) = ( 2 b − a b + 1 ) \sum_t \binom{t-1}{a-1}\binom{2b-a-t}{b-a+1} = \binom{2b-a}{b+1} ∑t(a−1t−1)(b−a+12b−a−t)=(b+12b−a) 。
对于左方的子问题,将值和值域倒过来就变成下方的子问题了。
#include <bits/stdc++.h>
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, a, b;
scanf("%d %d %d", &n, &a, &b);
vector<int> fac(n << 1), ifac(n << 1);
fac[0] = fac[1] = ifac[0] = ifac[1] = 1;
for (int i = 2; i < n << 1; ++i) {
fac[i] = mul(fac[i - 1], i);
ifac[i] = mul(md - md / i, ifac[md % i]);
}
for (int i = 2; i < n << 1; ++i) {
ifac[i] = mul(ifac[i - 1], ifac[i]);
}
auto binom = [&](int x, int y) {
if (x < y || x < 0 || y < 0) {
return 0;
}
return mul(fac[x], mul(ifac[y], ifac[x - y]));
};
auto solve = [&](int x, int y) {
return sub(binom((x << 1) - y, x), binom((x << 1) - y, x + 1));
};
if (a + b < n - 1) {
a = n - a - 1;
b = n - b - 1;
}
printf("%d\n", mul(solve(b, a + b + 1 - n), solve(a, a + b + 1 - n)));
return 0;
}