等差数列
CF763 C. Timofey and remoduling
当 \(n=m\) 时,答案显然为 1 1
。
当 \(n\neq m\) 时:
设 \(b_i\) 为没取模的原等差序列,公差为 \(d\),即 \(b_i = b_{i-1}+d,i\ge2\),\(p_i\) 满足 \(b_{p_i} \bmod m = a_i\),\(p_i\) 即为 \(a_i\) 在 \(b\) 中的下标。
等差数列有 \(b_j - b_i = (j - i) \times d\)。
设 \(w = p_2 - p_1\),\(w\) 即为 \(a_1,a_2\) 对应到 \(b\) 中这两项间隔项数,满足 \(b_j - b_i = w \times d\) 的 \((i, j)\) 共有 \(n - w\) 个:\((1, w+1),(2,w+2),(3,w+3),\dots,(n-w,n)\)。
对应到 \(a\) 为 \(a_2 - a_1 \equiv w \times d \pmod m\),统计满足 \(a_i - a_j \equiv w \times d \pmod m\) 的 \((i, j)\) 的个数,设 \(c\) 为其个数,有 \(c = n - w\),则可求出 \(d\):\(w = n - c\),\(d = (a_2 - a_1) \times w^{-1} \bmod m\)。
有了公差 \(d\),遍历每一项 \(a_i\),它的前一项为 \(a_i-d\),若 \(a\) 无与其同余的数,则 \(a_i\)为首项。
若有 \(p_i < p_j\),满足 \((a_i - a_j) \equiv w\times d \pmod m\),即 \(a_i,a_j\) 在 \(b\) 中对应的位置应为 \(a_i\) 在 \(a_j\) 前面,但在同余式子中体现出 \(a_i\) 在 \(a_j\) 后面,这是不对的,我们就多统计了一个 \((i, j)\),求错了 \(c\),从而导致答案错误。
满足 \((a_i - a_j) \equiv w\times d \pmod m\) 的一个必要不充分条件为:\(m|(w+p_i-p_j)\)。\(w+p_i-p_j \le 2n-2\),即当 \(m \le 2n-2\) 时,上述做法可能会出错;\(m > 2n-2\) 时,上述做法一定正确。
对于所有 \(1\le i < j\le m\):
对于所有 \(1\le i \le m\):
上述两个式子表明,等差数列 \(b\) 延长后,满足 \(\forall i\in \mathbb{N_+}\),\(b_i \sim b_{i+m-1}\) 为一个长度为 \(m\) 的排列。
将 \(a\) 对 \([0, m)\cap\mathbb{Z}\) 取补集得到长度为 \(n'\) 的集合 \(a'\)。\(a\) 和 \(a'\) 所在的等差数列相同。
用 \(a'\) 求一遍可求出公差 \(d\),以及 \(a'\) 的首项 \(a_1'\)。因为 \(\forall i\in \mathbb{N_+}\),\(b_i \sim b_{i+m-1}\) 为一个长度为 \(m\) 的排列。可以看做 \(a'\) 在 \(a\) 的左边,这样 \(a_1 = a_1'+n' \times d\),也可以看做 \(a\) 在 \(a\) 的左边,这样 \(a_1 = a_1' - n \times d\)。
总时间复杂度 \(O(n)\)。
逆序对数
简化题面:
给定一个正整数 \(n\),求所有长度为 \(n\) 且逆序对数也为 \(n\) 的排列个数,答案对 \(10^9+7\) 取模。\(1\le n\le 10^5\)。
设第 \(i\) 个数与 \(1\sim i-1\) 个数产生的逆序对数为 \(x_i\),则第 \(i\) 个数最少可以贡献 \(0\) 个逆序对,最多可以贡献 \(i-1\) 个逆序对,即 \(0\le x_i\le i-1\)。可以原问题转化成不定方程整数解问题:
求
的整数解个数。
换元 \(y_i \leftarrow x_i+1\)
求出 \(1\le y_i\) 时的答案
利用容斥原理求出不满足 \(y_i \le i\) 的方案数
\(+ ``y_1>1的方案数" +``y_2>2 的方案数" +\cdots+``y_n>n的方案数"\)
\(-``y_1>1且y_2>2的方案数`` - \cdots - ``y_{n-1}>n-1且y_n>n的方案数"\)
\(+``y_1>1且y_2>2且y_3>3的方案数+\cdots\)
\(\cdots\)
例如,\(y_1>1\) 的方案数可以从等式右边 \(2n\) 个数拿出一个数给 \(y_1\):
设不合法的 \(y_i\) 的下标 \(i\) 的集合为 \(S\),$\emptyset \neq
方案数:
直接枚举 \(S\) 时间复杂度为 \(O(2^n)\),考虑优化。
注意到式子的值只和 \(|S|\) 与 \(\sum_{i\in S}i\) 有关,与 \(S\) 具体包括何值无关。
设 \(f_{i,j}\) 为满足 \(i=|S|,j=\sum_{k\in S}k\) 的 \(S\) 方案数个数。
有
假设 \(f_{i,j}\) 代表的其中一集合为 \(\{a,b,\dots,c\}\),第一个式子为 \(\{a,b,\dots,c\} \rightarrow \{a+1,b+1,\dots,c+1\}\),第二个式子为 \(\{a,b,\dots,c\}\rightarrow\{1,a+1,b+1,\dots,c+1\}\),这样就可以遍历 \(\{1,2,3,\dots,n\}\) 的所有子集。
要确保 \(\sum_{k\in S}k \le n\) 这样 \(\binom{2n-1-\sum_{k\in S}k}{n-1}\) 才有值,由于 \(S\) 是集合,其数不互相同,\(|S|\) 最大的情况为 \(S=\{1,2,\dots,m\}\),其中 \(m\) 是满足 \(\sum_{k=1}^m k = \frac{(m+1)m}{2} \le n\) 的最大正整数,\(m=O(\sqrt n)\)。
则答案为
总时间复杂度为 \(O(n\sqrt n)\)。
#include <bits/stdc++.h>#define rep(it, st, ed) for(int it = (st); it <= (ed); it++)
#define per(it, st, ed) for(int it = (ed); it >= (st); it--)using ll = long long;template<class T>
void qr(T& x) {x = 0;bool s = 0;char c;while(!isdigit(c = getchar())) if(c == '-') s = 1;do x = (x << 3) + (x << 1) + (c ^ 48);while(isdigit(c = getchar()));if(s) x = -x;
}
template<class T>
void qw(T x) {static char _buf[100];int bp = 0;bool sgn = 0;if(!x) return (void)putchar('0');if(x < 0) x = -x, sgn = 1;while(x){_buf[++bp] = x % 10;x /= 10;}if(sgn) putchar('-');per(i, 1, bp) putchar(_buf[i] | 48);
}ll ksm(ll a, ll p, ll mod) {ll res = 1;while(p){if(p & 1) res = res * a % mod;a = a * a % mod;p >>= 1;}return res % mod;
}#define M (((ll)1e9) + 7)
inline int MOD(ll x) { return x >= M ? x - M : x; }
inline void ADD(ll& x, ll y) { x = MOD(x + y); }bool _st;#define N 200005int n;
ll inv[N], fac[N], v[N], f[500][N];
ll C(int x, int y) { return fac[x] * inv[y] % M * inv[x - y] % M; }bool _ed;
int main() {fac[0] = 1;rep(i, 1, N - 1) fac[i] = fac[i - 1] * i % M;inv[N - 1] = ksm(fac[N - 1], M - 2, M) % M;per(i, 0, N - 2) inv[i] = inv[i + 1] * (i + 1) % M;qr(n);rep(i, 0, n) v[i] = C(n + i - 1, n - 1);ll ans = v[n];int siz = 1;while(siz * (siz + 1) / 2 <= n) siz++;f[1][1] = 1;rep(i, 1, siz)rep(j, 1, n){if(i & 1) ADD(ans, M - f[i][j] * v[n - j] % M);else ADD(ans, f[i][j] * v[n - j] % M);if(j + i <= n) ADD(f[i][i + j], f[i][j]);if(j + i + 1 <= n) ADD(f[i + 1][i + j + 1], f[i][j]);}qw(ans);return 0;
}
联通
鸽
相似序列
简要题意:给定长度为 \(n\) 的正整数序列 \(a_i\),\(q\) 个询问 \(l_1, r_1, l_2, r_2\),判断 \(a_{l_1\sim r_1}\) 和 \(a_{l_2\sim r_2}\) 是否相似。
长度为 \(m\) 的序列 \(a\) 和 \(b\) 相似为:\(a,b\) 升序排序后,不同位置的数最多只有一个。
保证 \(1\le n,q,a_i\le 10^5, r_1-l_1=r_2-l_2\)。
用线段数可持久化权值线段树,哈希一下:给每个值赋个随机权值,当这个值的个数加一时,其哈希值就加其所赋的随机权值。
线段树上二分第一个不同的值 \(p\) 和 最后一个不同的值 \(q\)。若无不同则输出 YES
。
若不同,则 若其个数差是否小于等于一,且 \(a_{l_1\sim r_1}\) 和 \(b_{l_2, r_2}\) 无 \([p+1, q-1]\) 中的数,则输出 YES
,否则输出 NO
。
#include <bits/stdc++.h>#define rep(it, st, ed) for(int it = (st); it <= (ed); it++)
#define per(it, st, ed) for(int it = (ed); it >= (st); it--)
using ul = unsigned long long;template<class T>
void qr(T& x) {x = 0;bool s = 0;char c;while(!isdigit(c = getchar())) if(c == '-') s = 1;do x = (x << 3) + (x << 1) + (c ^ 48);while(isdigit(c = getchar()));if(s) x = -x;
}#define N 100005
#define V 100000struct SegTree {int l, r;int siz;int c;ul v;
} T[N * 20];
int rt[N], tp = 0;ul am[N];
void pushup(int p) {T[p].v = T[T[p].l].v + T[T[p].r].v;T[p].c = T[T[p].l].c + T[T[p].r].c;
}
void add(int& p, int q, int lv, int rv, int val) {p = ++tp;T[p] = T[q];if(lv == rv) {T[p].v += am[val];T[p].c++;return;}int mid = (lv + rv) >> 1;if(val <= mid)add(T[p].l, T[q].l, lv, mid, val);elseadd(T[p].r, T[q].r, mid + 1, rv, val);pushup(p);
}
::std::pair<int, int> askl(int p1, int q1, int p2, int q2, int l, int r) {if(l == r) return {T[p1].c - T[q1].c - (T[p2].c - T[q2].c), l};int ha = T[T[p1].l].v - T[T[q1].l].v;int hb = T[T[p2].l].v - T[T[q2].l].v;int mid = (l + r) >> 1;if(ha ^ hb) return askl(T[p1].l, T[q1].l, T[p2].l, T[q2].l, l, mid);else return askl(T[p1].r, T[q1].r, T[p2].r, T[q2].r, mid + 1, r);
}
::std::pair<int, int> askr(int p1, int q1, int p2, int q2, int l, int r) {if(l == r) return {T[p1].c - T[q1].c - (T[p2].c - T[q2].c), l};int ha = T[T[p1].r].v - T[T[q1].r].v;int hb = T[T[p2].r].v - T[T[q2].r].v;int mid = (l + r) >> 1;if(ha ^ hb) return askr(T[p1].r, T[q1].r, T[p2].r, T[q2].r, mid + 1, r);else return askr(T[p1].l, T[q1].l, T[p2].l, T[q2].l, l, mid);
}
int ask(int p, int q, int l, int r, int lq, int rq) {if(lq > rq) return 0;if(lq <= l && r <= rq) return T[p].c - T[q].c;int mid = (l + r) >> 1;int res = 0;if(lq <= mid)res += ask(T[p].l, T[q].l, l, mid, lq, rq);if(mid < rq)res += ask(T[p].r, T[q].r, mid + 1, r, lq, rq);return res;
}int n, q, a[N];::std::random_device rd;
::std::mt19937 gen(rd());int main() {qr(n), qr(q);rep(i, 1, V) am[i] = gen();rep(i, 1, n){qr(a[i]);add(rt[i], rt[i - 1], 1, V, a[i]);}while(q--){int l1, r1, l2, r2;qr(l1), qr(r1), qr(l2), qr(r2);#define x second#define y firstauto L = askl(rt[r1], rt[l1 - 1], rt[r2], rt[l2 - 1], 1, V);auto R = askr(rt[r1], rt[l1 - 1], rt[r2], rt[l2 - 1], 1, V);int cnt = ask(rt[r1], rt[l1 - 1], 1, V, L.x + 1, R.x - 1)+ ask(rt[r2], rt[l2 - 1], 1, V, L.x + 1, R.x - 1);if(L.y > 1 || L.y < -1 || R.y > 1 || R.y < -1 || cnt)puts("NO");elseputs("YES");#undef x#undef y}return 0;
}