Japan Alumni Group Summer Camp 2018 Day 2 简要题解

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 , ⋯ &ThinSpace; , 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 &lt; n , a ∈ S 1\le a &lt; n, a\in S 1a<n,aS ,因为 a + n &gt; n a + n &gt; n a+n>n 所以 n − a ∈ S n-a\in S naS

不妨假设 n 2 \frac{n}{2} 2n 不在集合中,那么 S = { 0 , a 1 , a 2 , ⋯ &ThinSpace; , a k , n − a k , n − a k − 1 , ⋯ &ThinSpace; , 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,nak,nak1,,na1,n}

k ≥ 2 k\ge 2 k2 ∀ 1 ≤ i &lt; j ≤ k \forall 1\le i &lt; j\le k 1i<jk ( n − a i ) + ( n − a j ) &gt; n (n - a_i) + (n - a_j) &gt; n (nai)+(naj)>n 所以 a j − a i ∈ S a_j - a_i\in S ajaiS

又因为 a j − a i &lt; a j ≤ a k a_j - a_i &lt; a_j\le a_k ajai<ajak 所以 a j − a i ≤ { a 1 , a 2 , ⋯ &ThinSpace; , a k − 1 } a_j - a_i\le \{a_1, a_2, \cdots, a_{k-1}\} ajai{a1,a2,,ak1}

那么 a 2 − a 1 &lt; a 3 − a 1 &lt; a 4 − a 1 &lt; ⋯ &lt; a k − a 1 ∈ { a 1 , a 2 , ⋯ &ThinSpace; , a k − 1 } a_2 - a_1 &lt; a_3 - a_1 &lt; a_4 - a_1 &lt; \cdots &lt; a_k - a_1 \in \{a_1, a_2, \cdots, a_{k-1}\} a2a1<a3a1<a4a1<<aka1{a1,a2,,ak1}

只能有 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 a2a1=a3a2=a4a3==akak1=a1

又因为 n − a 1 − a k ∈ S n - a_1 - a_k\in S na1akS 可以推出 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 &lt; x , 0 ≤ v &lt; y ) uy - vx = -1(0\le u &lt; x, 0\le v &lt; y) uyvx=1(0u<x,0v<y) ,那么 ( u , v ) (u, v) (u,v) 一定是一组解。

证明:

首先可以证明 gcd ⁡ ( x − u , y − v ) = 1 \gcd(x-u, y-v) = 1 gcd(xu,yv)=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)(2ux,2vy) 内部也没有整点:边界多了 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=n1 的情况。

a + b &lt; n − 1 a + b &lt; n - 1 a+b<n1 时,可以将序列和值域都反转, a ′ = n − a − 1 , b ′ = n − b − 1 a&#x27; = n - a - 1, b&#x27; = n - b - 1 a=na1,b=nb1

( i , p i ) (i, p_i) (i,pi) 在平面上画出来, ( a , b ) (a, b) (a,b) 将平面分成了四个区域,不难发现左下和右上不能同时有点。

假设左下没有点,那么左上有 a a a 个点,右下有 b b b 个点,右上有 n − 1 − a − b &lt; 0 n - 1 - a - b &lt; 0 n1ab<0 个点,不合法。

不难发现左下的点一定是单调递减的,可以将问题划分成下方和左方两个子问题,相当于要解决 b = n − 1 b = n - 1 b=n1 的情况。

一个合法的序列可以唯一对应一条从 ( 0 , n ) (0, n) (0,n) ( n , 0 ) (n, 0) (n,0) 的路径,满足任意时刻 x + y ≤ n x + y\le n x+yn ,路径长成 f ( x ) = min ⁡ i ≤ x p i f(x) = \min_{i\le x} p_i f(x)=minixpi 的形式。

那么 b = n − 1 b = n-1 b=n1 的情况相当于一个长度为 b b b 的合法序列,前 a a a 个位置都是前缀最小值。

a a a 个位置假设往下走了 t ( t ≥ a ) t(t\ge a) t(ta) 步,

前面就是方程 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} (a1t1)

后面是从 ( a , b − t ) (a, b-t) (a,bt) 走到 ( 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} (ba2bat)(ba+12bat)

∑ 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(a1t1)(ba2bat)=(b2ba)

组合解释是相当于从 2 b − a 2b-a 2ba 个数里面选 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(a1t1)(ba+12bat)=(b+12ba)

对于左方的子问题,将值和值域倒过来就变成下方的子问题了。

#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;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值