讲课总结
头不晕了,整体而言状态有所回升。基本都听得懂,也能写(除了之前wyp讲的一些数学)。但是做题的有时候脑子要抽风,往往最后一步转不过弯来。不过打的非正解经常会想到一些奇奇怪怪的优化拿到高分甚至于得到正解。
身体素质感觉比期末考试的时候好些了,跑环校路没什么压力。莫名奇妙的有些担心文化课(害怕初三翻车?)。
其实还留了很多坑没填(min25,各种奇怪多项式,LCT,SAM, 插头dp...),不会再讲的内容之后有空还是要补一下。
以下就是知识点总结:
数学
min-max容斥
其实就是这两个式子:
证明其实很简单,把式子展开,发现不是min/max的值都被抵消掉了。通常很容易知道其中一个但不知道另一个的时候有用。
没有固定的板子,看例题吧(P3175):
每秒会选择一个\([0,2^n-1]\)的数字与手上的数取或(其实可以看成一个集合),选\(i\)的概率为\(p_i\),求手上的数字变成\(2^n-1\)的期望时间。
Sol:
其实这个式子在期望下也成立,设\(\max(S)\)表示二进制数\(S\)中最后一个1被或出来的时间,\(\min(S)\)同理,则根据容斥:
考虑如何算\(E(\min(S))\),显然根据期望定义它是一次操作或出S中任意一个元素的倒数,则有:
直接做个FWT就行了。
#include <iostream>
#include <cstdio>
#include <algorithm>
#include <cstring>
using namespace std;
const int N = 1 << 21;
int n, fl[N];
double a[N + 1];
inline void OR(double *f) {
for (int i = 2, j = 1; i <= n; i <<= 1, j <<= 1)
for (int k = 0; k < n; k += i)
for (int l = 0; l < j; ++l)
f[j + k + l] += f[k + l];
}
int main() {
scanf("%d", &n); n = 1 << n;
for (int i = 0; i < n; ++i) scanf("%lf", &a[i]);
OR(a); double ans = 0.0;
fl[0] = -1;
for (int i = 1; i < n; ++i) {
fl[i] = (i & 1) ? -fl[i >> 1] : fl[i >> 1];
double t = a[(n - 1) ^ i];
if (1.0 - t < 1e-8) continue;
ans += 1.0 * fl[i] / (1.0 - t);
}
if (ans < 1e-7) puts("INF");
else printf("%.6lf", ans);
}
原根
感觉是个没啥用的东西,不过见过几次,还是写一下。
阶
满足\(a^n\equiv 1 \pmod m\)的最小整数\(n\),记作\(\delta_m(a)\)
原根
就是满足\(a\perp m\)且\(\delta_m(a)=phi(m)\)的\(m\)。
求原根的思路很暴力。设\(p\in Prime, k \in Z_{+}\),不加证明的给出则只有\(2,4,2\times p^k\)有原根,且\(n\)的原根个数是\(\varphi(\varphi(n))\)级别的。
首先枚举出最小原根\(g\),然后其它的原根一定是\(g^k\space\space(k\perp \varphi(n))\)。
如何判断一个数\(g\)是否为原根?直接按定义一个一个检验太麻烦。枚举\(n\)的所有素数因子\(p\),判断\(g^{\varphi(n)\over p}\neq 1\pmod m\)就行。
#include <iostream>
#include <cstdio>
#include <algorithm>
#include <cstring>
#include <vector>
using namespace std;
const int N = 1e6 + 5;
int prime[N], top = 0, phi[N];
bool notprime[N];
inline void pre() {
notprime[1] = 1; phi[1] = 1;
for (int i = 2; i <= N - 5; ++i) {
if (!notprime[i]) prime[++top] = i, phi[i] = i - 1;
for (int j = 1; j <= top && i * prime[j] <= N - 5; ++j) {
notprime[i * prime[j]] = 1;
if (i % prime[j]) phi[i * prime[j]] = phi[i] * (prime[j] - 1);
else {
phi[i * prime[j]] = phi[i] * prime[j];
break;
}
}
} //for (int i = 1; i <= 10; ++i) cout << phi[i] << ' ';
}
int T, n, d;
vector<int> fac, ans;
inline bool check(int x) {
if (x == 2 || x == 4) return 1;
if (!(x & 1)) x >>= 1;
if (!(x & 1)) return 0;
for (int i = 2; i <= top; ++i) {
if (!(x % prime[i])) {
while (!(x % prime[i])) x /= prime[i];
return x == 1;
}
} return 0;
}
inline int power(int a, int b, int p) {
int t = 1, y = a, k = b;
while (k) {
if (k & 1) t = (1ll * t * y) % p;
y = (1ll * y * y) % p; k >>= 1;
} return t % p;
}
inline int gcd(int a, int b) {
return !b ? a : gcd(b, a % b);
}
int main() {
pre();
scanf("%d", &T);
while (T--) {
scanf("%d%d", &n, &d);
if (!check(n)) puts("0\n");
else {
fac.clear(); ans.clear();
int ph = phi[n];
for (int i = 2; i * i <= ph; ++i) {
if (!(ph % i)) {
fac.push_back(i);
while (!(ph % i)) ph /= i;
}
} if (ph != 1) fac.push_back(ph);
int g;
for (int i = 1; ; ++i) {
if (gcd(phi[n], i) != 1) continue;
for (vector<int> :: iterator j = fac.begin(); j != fac.end(); ++j)
if (power(i, phi[n] / *j, n) == 1) goto Fail;
g = i; break;
Fail:;
}
const int t = g;
for (int i = 1; ans.size() < phi[phi[n]]; ++i, g = (1ll * g * t) % n)
if (gcd(phi[n], i) == 1) ans.push_back(g);
sort(ans.begin(), ans.end());
printf("%d\n", ans.size());
for (int i = d - 1; i < ans.size(); i += d) printf("%d ", ans[i]);
puts("");
}
}
return 0;
}
二次剩余
即解\(x^2\equiv n \pmod p\),p为奇素数,有解则称\(n\)为二次剩余。
俗称模意义开根。初中学的完全平方公式可知有两个解,且模\(p\)意义下互为相反数。
没怎么见过,大概率一辈子也碰不到。只给出结论:
首先若\(n\)为非二次剩余的充要条件(欧拉准则):\(n^{p-1\over 2}\equiv -1 \pmod p\)
然后我们随机出一个\(a\),满足\(a^2-n\)为非二次剩余,我们扩充虚数域,令\(i^2=a^2-n\),则一个解为\((a+i)^{p-1\over 2} \bmod p\)。另一个解就是它的相反数。
#include <iostream>
#include <cstdio>
#include <algorithm>
#include <cstring>
#include <ctime>
using namespace std;
int n, p, T, pt;
struct complex {
int rl, cp;
// inline complex(int R, int C) : rl(R), cp(C) {}
};
inline complex operator * (complex a, complex b) {
complex cc;
cc.rl = ((1ll * a.rl * b.rl + 1ll * pt * a.cp % p * b.cp) % p + p) % p;
cc.cp = (1ll * a.rl * b.cp + 1ll * a.cp * b.rl) % p;
return cc;
}
inline int power(int a, int b) {
int k = b, y = a, t = 1;
while (k) {
if (k & 1) t = (1ll * t * y) % p;
y = (1ll * y * y) % p; k >>= 1;
} return t;
}
inline complex cpower(complex a, int b) {
int k = b; complex y = a, t;
t.rl = 1; t.cp = 0;
while (k) {
if (k & 1) t = t * y;
y = y * y; k >>= 1;
} return t;
}
int main() {
scanf("%d", &T);
while (T--) {
scanf("%d%d", &n, &p);
if (power(n, p - 1 >> 1) == p - 1) puts("Hola!");
else {
srand((unsigned)time(0)); int a = rand() % p; int t = ((1ll * a * a % p - n) % p + p) % p;
while (power(t, p - 1 >> 1) == 1) { a = rand() % p; t = ((1ll * a * a % p - n) % p + p) % p; }
int ans1, ans2; pt = t;
complex tmp; tmp.rl = a; tmp.cp = 1;
tmp = cpower(tmp, p + 1 >> 1);
ans1 = tmp.rl;
ans2 = (p - ans1) % p;
if (ans1 > ans2) swap(ans1, ans2);
if (ans1 == ans2) printf("%d\n", ans1);
else printf("%d %d\n", ans1, ans2);
}
}
return 0;
}
莫比乌斯反演
先说狄利克雷卷积:
容易自证:\((\varphi \cdot 1)(n)=n\),\((\mu \cdot 1)(n)=[n=1]\)
那么有结论:
为啥?定义式带进去就行。
但感觉好多题用的都不是这个,而是上面两个。感觉每次都是枚举因数,用上面两个式子,然后求和号凭感觉换来换去。
题单:https://www.luogu.com.cn/training/1055#problems
杜教筛
求积性函数前缀和。
设\(S(n)=\sum_{i=1}^n f(i)\),\(h=f\cdot g\)
又卷积定义可得:
所以:
所以关键就是找出合适的\(g\)。
给个求\(\phi\)的板子:
ll getphi(ll n) {
if (phi.find(n) != phi.end()) return phi[n];
ll rt = n * (n + 1) / 2;
for (register int l = 2, r; l <= n; l = r + 1) {
r = n / (n / l);
rt -= (r - l + 1) * getphi((ll)n / l);
}
return phi[n] = rt;
}
狄利克雷生成函数
那么显然黎曼函数就是\(1\)的DGF:
常见数论函数的狄利克雷生成函数都可以自己手推,在这里给出一个欧拉函数的DGF来演示常规推导思路:
由于欧拉函数的积性,考虑枚举质数:
我们又有:
所以原式为:
其实求DGF并不需要在每个值上都有显性的式子,只要\(f(p^k)\)有定义且为积性函数就,那么它的DGF就为:
关于这个DGF,还有一个重要性质:
设\(\otimes\)表示狄利克雷卷积,则
证明很显然:
这个也可以用来筛前缀和(min25筛没听懂,感觉一些时候能够代替)。也可以配合杜教筛较为模板化的找到更好的g。
以求积性函数\(f(p^k)=p^k(p^k-1)\)(p为素数)前缀和为例:
先考虑求出\(f\)的DGF:
里面的式子瞎化简完:
仔细观察,分母实际上是\(1\over\zeta(x-2)\zeta(x-1)\)
所以原式为:
我们左边看起来是个很清爽的式子,但右边很麻烦。我们DGF一般要弄成:
的形式。
右边那一坨,刚才手动展开了化简过程的应该可以看出来:
为了得到常数1,我们在里面除一个\((1-p^{1-x})^2\),相当于在外面乘了\((1-p^{1-x})^2\),式子变成:
把分式拆开,发现:
有什么用呢???
我们之前有性质,两函数DGF的乘积为两函数狄利克雷卷积的DGF。
令:
那么\(f=g\otimes h\),则:
我们观察上面的DGF,显然可以得到\(f(p^i)=(i-1)(p^{i+1}-p^i)\),且\(f(p)=0\)
所以\(h(x)\)不为0的\(x\)必定是个Powerful Number,即每个质因子的次数都大于2.可以证明它的个数不超过\(\sqrt{n}\),我们可以打出质数表搜索,关键在求\(\sum_{i=1}^{\lfloor{n\over d}\rfloor} g(i)\)上。直接做很难搞。
考虑当\(p\in Prime\)时:
所以\(g(p)=p(p-1)\),同时他的DGF长的也很像\(\varphi\)的DGF,我们可以猜测\(g(x)=x\varphi(x)\),然后把它的DGF求出来发现是对的。而他的前缀和我们可以杜教筛。
#include <iostream>
#include <cstdio>
#include <algorithm>
#include <cstring>
#include <unordered_map>
#include <map>
using namespace std;
const int LIM = 4641589, mod = 1e9 + 7, INV = 1e9 + 8;
inline int power(int a, int b) {
int t = 1, y = a, k = b;
while (k) {
if (k & 1) t = (1ll * t * y) % mod;
y = (1ll * y * y) % mod; k >>= 1;
} return t;
}
typedef long long ll;
bool notprime[LIM + 5];
int prime[LIM + 5], top, g[LIM + 5], sg[LIM + 5];//, lim;
ll n;
unordered_map<ll, int> sumg;
inline void pre() {
notprime[0] = notprime[1] = 1; g[1] = 1;
for (register int i = 2; i <= LIM; ++i) {
if (!notprime[i]) prime[++top] = i, g[i] = i - 1;
for (register int j = 1; j <= top && i * prime[j] <= LIM; ++j) {
notprime[i * prime[j]] = 1;
if (!(i % prime[j])) {
g[i * prime[j]] = (1ll * g[i] * prime[j]) % mod;
break;
} g[i * prime[j]] = (1ll * g[i] * (prime[j] - 1)) % mod;
}
} sg[0] = 0; sg[1] = g[1];
for (register int i = 2; i <= LIM; ++i) sg[i] = (sg[i - 1] + ((1ll * i * g[i]) % mod)) % mod;
}
inline int getsum(ll n) {
if (n <= LIM) return sg[n];
if (sumg[n]) return sumg[n];
register int ans = (1ll * ((1ll * ((1ll * (n % mod) * ((n + 1) % mod)) % mod) * ((n * 2ll + 1) % mod)) % mod) * (INV / 6)) % mod;
for (register ll l = 2, r; l <= n; l = r + 1) {
r = n / (n / l);
int d = (1ll * ((r - l + 1) % mod) * ((r + l) % mod)) % mod;
d = (1ll * d * (INV >> 1)) % mod;
d = (1ll * d * getsum(n / l)) % mod;
ans -= d; ans %= mod;
if (ans < 0) ans += mod;
} return sumg[n] = ans;
}
int ans = 0;
inline void dfs(int now, ll num, int h) {
if (now > top || num * prime[now] > n || num * prime[now] * prime[now] > n) {
int del = h; del = (1ll * del * ((n / num) <= LIM ? sg[n / num] : sumg[n / num])) % mod;
ans = (ans + del) % mod;
return ;
} dfs(now + 1, num, h);
register ll p = 1ll * prime[now] * prime[now];
register int t = 2, hp;
while (num * p <= n) {
hp = (1ll * (p % mod) * prime[now]) % mod;
hp = (hp - (p % mod)) % mod;
if (hp < 0) hp += mod;
hp = (1ll * hp * (t - 1)) % mod;
dfs(now + 1, num * p, (1ll * h * hp) % mod);
++t; p = p * prime[now];
}
}
int main() {
scanf("%lld", &n);
pre(); getsum(n);
dfs(1, 1, 1);
printf("%d", ans);
return 0;
}
多项式
不多说了,关键在于板子:
#include <iostream>
#include <cstdio>
#include <algorithm>
#include <cstring>
#include <vector>
#include <cmath>
using namespace std;
const int N = 1e6 + 5, mod = 998244353, G = 3, Gi = 332748118;
inline int read() {
int s = 0, f = 1;
char ch = getchar();
while (!isdigit(ch)) f = (ch == '-' ? -1 : 1), ch = getchar();
while (isdigit(ch)) s = (s << 1) + (s << 3) + (ch & 15), ch = getchar();
return s * f;
}
int rev[N], l = 1;
inline int power(int a, int b) {
register int k = b, y = a, t = 1;
while (k) {
if (k & 1) t = (1ll * t * y) % mod;
y = (1ll * y * y) % mod; k >>= 1;
} return t;
}
struct poly {
int n;
vector<int> x;
inline void NTT(int flag) {
for (register int i = 0; i < n; ++i)
if (i < rev[i]) swap(x[i], x[rev[i]]);
for (register int mid = 1; mid < n; mid <<= 1) {
int wn = power(flag == 1 ? G : Gi, (mod - 1) / (mid << 1));
for (register int j = 0; j < n; j += mid << 1) {
int bas = 1;
for (register int k = 0; k < mid; ++k, bas = (1ll * bas * wn) % mod) {
int xx = x[j + k], y = (1ll * bas * x[j + mid + k]) % mod;
x[j + k] = xx + y;
if (x[j + k] >= mod) x[j + k] -= mod;
x[j + mid + k] = xx - y;
if (x[j + mid + k] < 0) x[j + mid + k] += mod;
}
}
}
}
// poly() { memset(x, 0, sizeof x); }
}a, b;
inline int max_(int a, int b) {
return a > b ? a : b;
}
inline poly mul(poly A, poly B) {
poly a, b; a = A; b = B;
int tmp = a.n + b.n; a.n = 1;
int L = 0;
while (a.n <= tmp) a.n <<= 1, ++L;
for (register int i = 0; i <= a.n; ++i) rev[i] = (rev[i >> 1] >> 1) | ((i & 1) << L - 1);
b.n = a.n;
a.NTT(1); b.NTT(1);
for (register int i = 0; i < a.n; ++i) a.x[i] = (1ll * a.x[i] * b.x[i]) % mod;
a.NTT(-1);
const int inv = power(a.n, mod - 2);
a.n = tmp;
for (register int i = 0; i <= a.n; ++i) a.x[i] = (1ll * a.x[i] * inv) % mod;
return a;
}
inline poly inv(poly B) {
poly a, A; A = B;
a.n = 1;
a.x.resize(N); poly b;
a.x[0] = power(A.x[0], mod - 2);
while (a.n < A.n) {
b = a;
b = mul(b, b);
for (register int i = 0; i <= a.n; ++i) {
a.x[i] <<= 1;
if (a.x[i] >= mod) a.x[i] -= mod;
} b = mul(A, b); a.n <<= 1;
for (register int i = 0; i <= a.n; ++i) {
a.x[i] -= b.x[i];
if (a.x[i] < 0) a.x[i] += mod;
}
} a.n = B.n;
return a;
}
inline poly der(poly A) {
poly d; d.x.resize(N); d.n = A.n - 1;
for (int i = 1; i <= A.n; ++i)
d.x[i - 1] = (1ll * i * A.x[i]) % mod;
return d;
}
inline poly integeral(poly A) {
poly t; t.x.resize(N); t.n = A.n + 1;
for (int i = 1; i <= t.n; ++i)
t.x[i] = (1ll * A.x[i - 1] * power(i, mod - 2)) % mod;
t.x[0] = 0; return t;
}
inline poly ln(poly A) {
poly G = integeral(mul(der(A), inv(A)));
G.n = A.n;
return G;
}
int main() {
a.n = read(); --a.n; a.x.resize(N);
for (register int i = 0; i <= a.n; ++i) a.x[i] = read();
b = ln(a);
for (register int i = 0; i <= b.n; ++i) printf("%d ", b.x[i]);
return 0;
}
以下就全是板子了(难懂的会提两句,太简单的板子就没列出来)
数据结构
线段树(P3373):
#include <iostream>
#include <cstdio>
#include <algorithm>
#include <cstring>
using namespace std;
const int N = 2e5 + 5, M = 2e5 + 5;
typedef long long ll;
int n, m, p, a[N];
ll tree[N << 2], add[N << 2], mul[N << 2];
inline int read() {
int s = 0, f = 1;
char ch = getchar();
while (!isdigit(ch)) f = (ch == '-' ? -1 : 1), ch = getchar();
while (isdigit(ch)) s = ((s << 1) + (s << 3) + (ch & 15)) % p, ch = getchar();
return ((s * f) % p + p) % p;
}
inline void build(int now, int l, int r) {
add[now] = 0; mul[now] = 1;
if (l >= r) { tree[now] = a[l]; return ; }
int mid = l + r >> 1;
build(now << 1, l, mid);
build(now << 1 | 1, mid + 1, r);
tree[now] = (tree[now << 1] + tree[now << 1 | 1]) % p;
return ;
}
inline ll qmul(ll a, ll b) {
return (a * b) % p;
}
inline ll qplus(ll a, ll b) {
return (a + b) % p;
}
inline void update(int now) {
tree[now] = tree[now << 1] + tree[now << 1 | 1];
}
inline void pushdown(int now, int l, int r) {
if (!add[now] && mul[now] == 1) return ;
add[now << 1] = qmul(add[now << 1], mul[now]);
add[now << 1] = qplus(add[now << 1], add[now]);
mul[now << 1] = qmul(mul[now << 1], mul[now]);
add[now << 1 | 1] = qmul(add[now << 1 | 1], mul[now]);
add[now << 1 | 1] = qplus(add[now << 1 | 1], add[now]);
mul[now << 1 | 1] = qmul(mul[now << 1 | 1], mul[now]);
int mid = l + r >> 1;
tree[now << 1] = qmul(tree[now << 1], mul[now]);
tree[now << 1] = qplus(tree[now << 1], qmul(add[now], mid - l + 1));
tree[now << 1 | 1] = qmul(tree[now << 1 | 1], mul[now]);
tree[now << 1 | 1] = qplus(tree[now << 1 | 1], qmul(add[now], r - mid));
add[now] = 0; mul[now] = 1;
}
inline void modify1(int now, int l, int r, int ql, int qr, ll c) {
pushdown(now, l, r);
if (ql <= l && r <= qr) {
add[now] = qplus(add[now], c);
tree[now] = qplus(tree[now], qmul(c, (r - l + 1)));
return ;
} int mid = l + r >> 1;
if (ql <= mid) modify1(now << 1, l, mid, ql, qr, c);
if (qr > mid) modify1(now << 1 | 1, mid + 1, r, ql, qr, c);
update(now);
}
inline void modify2(int now, int l, int r, int ql, int qr, ll c) {
pushdown(now, l, r);
if (ql <= l && r <= qr) {
tree[now] = qmul(tree[now], c);
add[now] = qmul(add[now], c);
mul[now] = qmul(mul[now], c);
return ;
} int mid = l + r >> 1;
if (ql <= mid) modify2(now << 1, l, mid, ql, qr, c);
if (qr > mid) modify2(now << 1 | 1, mid + 1 , r, ql, qr, c);
update(now);
}
inline ll query(int now, int l, int r, int ql, int qr) {
pushdown(now, l, r);
if (ql <= l && r <= qr) return tree[now];
int mid = l + r >> 1; ll ret = 0;
if (ql <= mid) ret = qplus(query(now << 1, l, mid, ql, qr), ret);
if (qr > mid) ret = qplus(query(now << 1 | 1, mid + 1, r, ql, qr), ret);
return ret;
}
int main() {
scanf("%d%d%d", &n, &m, &p);
for (int i = 1; i <= n; ++i) a[i] = read();
build(1, 1, n);
int opt, l, r, c;
while (m--) {
opt = read(); l = read(); r = read();
if (opt == 1) {
c = read();
modify2(1, 1, n, l, r, c);
} else if (opt == 2) {
c = read();
modify1(1, 1, n, l, r, c);
} else if (opt == 3)
printf("%lld\n", query(1, 1, n, l, r) % p);
} return 0;
}
线段树合并(P4556):
#include <iostream>
#include <cstdio>
#include <algorithm>
#include <cstring>
using namespace std;
const int N = 1e5 + 5;
int en = 0;
struct edge {
int head, to, nxt;
} ed[N << 1];
inline void addedge(int from, int to) {
ed[++en].to = to; ed[en].nxt = ed[from].head; ed[from].head = en;
}
int fa[N][20], dep[N];
inline void dfs(int now, int f) {
fa[now][0] = f; dep[now] = dep[f] + 1;
for (int i = 1; i < 20; ++i) fa[now][i] = fa[fa[now][i - 1]][i - 1];
for (int i = ed[now].head; i; i = ed[i].nxt) {
int v = ed[i].to;
if (v == f) continue;
dfs(v, now);
}
}
inline int lca(int a, int b) {
int u = a, v = b;
if (dep[u] < dep[v]) swap(u, v);
int t = dep[u] - dep[v];
for (int i = 0; i < 20; ++i)
if ((t >> i) & 1) u = fa[u][i];
if (u == v) return u;
for (int i = 19; ~i; --i)
if (fa[u][i] != fa[v][i])
u = fa[u][i], v = fa[v][i];
return fa[u][0];
}
struct node {
int mx, mxd;
node *left = NULL;
node *right = NULL;
node() { mx = mxd = 0; }
} *root[N];
inline node* update(node *now) {
if (now -> left == NULL && now -> right == NULL) return now;
if (now -> left == NULL) { now -> mx = now -> right -> mx; now -> mxd = now -> right -> mxd; return now; }
if (now -> right == NULL){ now -> mx = now -> left -> mx; now -> mxd = now -> left -> mxd; return now; }
if (now -> left -> mx < now -> right -> mx) { now -> mx = now -> right -> mx; now -> mxd = now -> right -> mxd; return now; }
if (now -> right -> mx <= now -> left -> mx) { now -> mx = now -> left -> mx; now -> mxd = now -> left -> mxd; return now; }
return now;
}
inline node* insert(node *now, int l, int r, int q, int k) {
if (now == NULL) {
now = new node;
}
if (l >= r) {
now -> mx += k;
now -> mxd = q;
return now;
}
if (q <= (l + r >> 1)) now -> left = insert(now -> left, l, l + r >> 1, q, k);
else now -> right = insert(now -> right, (l + r >> 1) + 1, r, q, k);
now = update(now);
return now;
}
inline int query(node *now, int l, int r, int q) {
if (now == NULL) return 0;
if (l >= r) return now -> mx;
int mid = l + r >> 1;
if (q <= mid) {
if (now -> left == NULL) return 0;
int t = query(now -> left, l, mid, q);
return t;
} else {
if (now -> right = NULL) return 0;
int t = query(now -> right, mid + 1, r, q);
return t;
}
}
inline node* merge(node *rta, node *rtb, int l, int r) {
if (rta == NULL) return rtb;
if (rtb == NULL) return rta;
node *ret = new node;
if (l >= r) {
ret -> mx = rta -> mx + rtb -> mx;
ret -> mxd = l;
return ret;
} ret -> left = merge(rta -> left, rtb -> left, l, l + r >> 1);
ret -> right = merge(rta -> right, rtb -> right, (l + r >> 1) + 1, r);
ret = update(ret);
return ret;
}
int ans[N], n, m, a, b, x, y, z;
inline int read() {
register int s = 0;
register char ch = getchar();
while (!isdigit(ch)) ch = getchar();
while (isdigit(ch)) s = (s << 1) + (s << 3) + (ch & 15), ch = getchar();
return s;
}
inline void calc(int now, int f) {
for (int i = ed[now].head; i; i = ed[i].nxt) {
int v = ed[i].to;
if (v == f) continue;
calc(v, now);
root[now] = merge(root[now], root[v], 1, N - 5);
} if (root[now] == NULL) ans[now] = 0;
else ans[now] = root[now] -> mxd * (root[now] -> mx > 0);
}
int main() {
n = read(); m = read();
for (int i = 1; i < n; ++i) {
a = read(); b = read();
addedge(a, b); addedge(b, a);
root[i] = NULL;
} memset(fa, 0, sizeof fa); dfs(1, 0); root[n] = NULL;
while (m--) {
x = read(); y = read(); z = read();
int L = lca(x, y);
root[x] = insert(root[x], 1, N - 5, z, 1);
root[y] = insert(root[y], 1, N - 5, z, 1);
root[L] = insert(root[L], 1, N - 5, z, -1);
if (fa[L][0] != 0) {
L = fa[L][0];
root[L] = insert(root[L], 1, N - 5, z, -1);
}
} calc(1, 0);
for (int i = 1; i <= n; ++i) printf("%d\n", ans[i]);
return 0;
}
主席树(静态区间k大)
#include <iostream>
#include <cstdio>
#include <algorithm>
#include <cstring>
using namespace std;
const int N = 3e5 + 5;
int n, m, a[N], srted[N], mp[N], l, r, k;
struct node {
int sum;
node *right = NULL;
node *left = NULL;
} mem[N * 20];
node *root[N];
int top = 0;
inline node* newNode(node *t) {
node *rt = &mem[++top]; rt -> sum = t -> sum; return rt;
}
inline void build(node *now, int l, int r) {
now -> sum = 0;
if (l >= r) return ;
now -> left = &mem[++top]; build(now -> left, l, l + r >> 1);
now -> right = &mem[++top]; build(now -> right, (l + r >> 1) + 1, r);
}
inline node* insert(node *now, node *rt, int l, int r, int k) {
rt = newNode(now); rt -> sum++;
if (l < r) {
int mid = l + r >> 1;
if (k <= mid) {
rt -> left = insert(now -> left, rt -> left, l, mid, k), rt -> right = now -> right;
} else {
rt -> right = insert(now -> right, rt -> right, mid + 1, r, k), rt -> left = now -> left;
}
} return rt;
}
inline int query(node *rta, node *rtb, int l, int r, int k) {
if (l >= r) return l; int mid = l + r >> 1;
if (rta -> left -> sum - rtb -> left -> sum >= k) return query(rta -> left, rtb -> left, l, mid, k);
return query(rta -> right, rtb -> right, mid + 1, r, k - rta -> left -> sum + rtb -> left -> sum);
}
int main() {
scanf("%d%d", &n, &m);
root[0] = &mem[++top]; build(root[0], 0, n + 5); int zym = 0;
for (int i = 1; i <= n; ++i) scanf("%d", &a[i]), srted[++zym] = a[i];
sort(srted + 1, srted + zym + 1);
for (int i = 1; i <= n; ++i) {
int k = lower_bound(srted + 1, srted + zym + 1, a[i]) - srted + 1;
mp[k] = a[i]; a[i] = k;
root[i] = insert(root[i - 1], root[i], 0, n + 5, k);
} while (m--) {
scanf("%d%d%d", &l, &r, &k);
printf("%d\n", mp[query(root[r], root[l - 1], 0, n + 5, k)]);
}
return 0;
}
树套树(P3380)
#pragma GCC target("avx2")
#include <iostream>
#include <cstdio>
#include <algorithm>
#include <cstring>
#include <vector>
using namespace std;
const int N = 5e4 + 5;
int a[N], n, m;
vector<int> vec[N << 2];
inline void merge(int x) {
int l = x << 1, r = x << 1 | 1;
vector<int> :: iterator itl = vec[l].begin(), itr = vec[r].begin();
while (itl != vec[l].end() || itr != vec[r].end()) {
if (itr == vec[r].end() || (itl != vec[l].end() && *itl < *itr)) vec[x].push_back(*itl), ++itl;
else vec[x].push_back(*itr), ++itr;
}
}
inline void build(int now, int l, int r) {
if (l >= r) {
vec[now].push_back(a[l]);
return ;
} build(now << 1, l, l + r >> 1);
build(now << 1 | 1, (l + r >> 1) + 1, r);
merge(now);
}
inline void modify(int now, int l, int r, int q, int k) {
vector<int> :: iterator it = lower_bound(vec[now].begin(), vec[now].end(), a[q]);
vec[now].erase(it);
it = upper_bound(vec[now].begin(), vec[now].end(), k);
vec[now].insert(it, k);
if (l >= r) return ;
int mid = l + r >> 1;
if (q <= mid) modify(now << 1, l, mid, q, k);
else modify(now << 1 | 1, mid + 1, r, q, k);
}
inline int rank(int now, int l, int r, int ql, int qr, int k) {
if (ql <= l && r <= qr) return lower_bound(vec[now].begin(), vec[now].end(), k) - vec[now].begin();
int ret = 0, mid = l + r >> 1;
if (ql <= mid) ret = rank(now << 1, l, mid, ql, qr, k);
if (qr > mid) ret += rank(now << 1 | 1, mid + 1, r, ql, qr, k);
return ret;
}
inline int kth(int l, int r, int k) {
int ll = 0, rr = 1e8, mid, ret = 0;
while (ll <= rr) {
mid = ll + rr >> 1;
if (rank(1, 1, n, l, r, mid) < k) ll = (ret = mid) + 1;
else rr = mid - 1;
} return ret;
}
inline int max_(int a, int b) {
return a > b ? a : b;
}
inline int min_(int a, int b) {
return a < b ? a : b;
}
inline int pre(int now, int l, int r, int ql, int qr, int k) {
if (ql <= l && r <= qr) {
vector<int> :: iterator it = lower_bound(vec[now].begin(), vec[now].end(), k);
if (it == vec[now].begin()) return -2147483647;
return *(--it);
} int ret = -2147483647;
int mid = l + r >> 1;
if (ql <= mid) ret = pre(now << 1, l, mid, ql, qr, k);
if (qr > mid) ret = max_(ret, pre(now << 1 | 1, mid + 1, r, ql, qr, k));
return ret;
}
inline int suf(int now, int l, int r, int ql, int qr, int k) {
if (ql <= l && r <= qr) {
vector<int> :: iterator it = upper_bound(vec[now].begin(), vec[now].end(), k);
if (it == vec[now].end()) return 2147483647;
return *it;
} int ret = 2147483647;
int mid = l + r >> 1;
if (ql <= mid) ret = suf(now << 1, l, mid, ql, qr, k);
if (qr > mid) ret = min_(ret, suf(now << 1 | 1, mid + 1, r, ql, qr, k));
return ret;
}
inline int read() {
register int s = 0;
register char ch = getchar();
while (!isdigit(ch)) ch = getchar();
while (isdigit(ch)) s = (s << 1) + (s << 3) + (ch & 15), ch = getchar();
return s;
}
int main() {
n = read(); m = read();
int opt, l, r, k;
for (int i = 1; i <= n; ++i) a[i] = read();
build(1, 1, n);
while (m--) {
opt = read(); l = read(); r = read();
if (opt == 1) printf("%d\n", rank(1, 1, n, l, r, k = read()) + 1);
else if (opt == 2) printf("%d\n", kth(l, r, k = read()));
else if (opt == 3) modify(1, 1, n, l, r), a[l] = r;
else if (opt == 4) printf("%d\n", pre(1, 1, n, l, r, k = read()));
else if (opt == 5) printf("%d\n", suf(1, 1, n, l, r, k = read()));
}
return 0;
}
用它优化建图(NOI2019 弹跳)
#include <iostream>
#include <algorithm>
#include <cstring>
#include <vector>
#include <cstdio>
#include <queue>
#include <set>
using namespace std;
const int N = 7e4 + 5, M = 1e5 + 5e4 + 5;
#define VIT vector<int>::iterator
#define SIT set<node>::iterator
inline int read() {
register int s = 0;
register char ch = getchar();
while (!isdigit(ch)) ch = getchar();
while (isdigit(ch)) s = (s << 1) + (s << 3) + (ch & 15), ch = getchar();
return s;
}
struct node {
int x, y;
inline node(int X, int Y) : x(X), y(Y) { }
inline bool operator < (const node &b) const { return y == b.y ? x < b.x : y < b.y; }
inline bool operator > (const node &b) const { return y == b.y ? x > b.x : y > b.y; }
};
set<node> tree[N << 2];
vector<int> nxt[N];
int px[N], py[N], n, m, w, h, p[M], t[M], L[M], R[M], D[M], U[M], dis[N + M], vis[N + M];
inline void insert(int now, int l, int r, int q) {
//cerr << now << ' ' << l << ' ' << r << ' ' << q << endl;
tree[now].insert(node(q, py[q]));
// cerr << "elts in node now : ";
// for (SIT it = tree[now].begin(); it != tree[now].end(); ++it)
// cerr << it -> x << ' ';
// cerr << endl;
if (l >= r) return ; int mid = l + r >> 1;
if (px[q] <= mid) insert(now << 1, l, mid, q);
else insert(now << 1 | 1, mid + 1, r, q);
}
priority_queue<node, vector<node>, greater<node> > q;
inline void relax(int now, int l, int r, int x) {
// cerr << now << ' ' << l << ' ' << r << ' ' << L[x] << ' ' << R[x] << endl;
if (L[x] <= l && r <= R[x]) {
// cerr << "Now Relax! : ";
for (SIT it = tree[now].lower_bound(node(0, D[x])), tmp; it != tree[now].end() && it -> y <= U[x];) {
int v = it -> x;
// cerr << v << ' ';
if (dis[v] > dis[x + n])
q.push(node(v, dis[v] = dis[x + n]));
tmp = it; ++it; tree[now].erase(tmp);
} //cerr << endl;
return ;
} int mid = l + r >> 1;
if (L[x] <= mid) relax(now << 1, l, mid, x);
if (R[x] > mid) relax(now << 1 | 1, mid + 1, r, x);
}
int main() {
// freopen("jump.in", "r", stdin); freopen("jump.out", "w", stdout);
n = read(); m = read(); w = read(); h = read();
for (int i = 1; i <= n; ++i) {
px[i] = read(); py[i] = read();
// cerr << "INSERTING : " << px[i] << ' ' << py[i] << ' ' << i << endl;
insert(1, 1, w, i);
} for (int i = 1; i <= n + m; ++i) dis[i] = 0x3f3f3f3f;
for (int i = 1; i <= m; ++i) {
p[i] = read(); t[i] = read(); L[i] = read(); R[i] = read(); D[i] = read(); U[i] = read();
nxt[p[i]].push_back(i);
} dis[1] = 0; q.push(node(1, 0));
while (!q.empty()) {
int x = q.top().x; q.pop();
if (vis[x]) continue; vis[x] = 1;
if (x <= n) {
for (VIT it = nxt[x].begin(); it != nxt[x].end(); ++it) {
int v = *it;
if (dis[v + n] > dis[x] + t[v])
q.push(node(v + n, dis[v + n] = dis[x] + t[v]));
}
} else relax(1, 1, w, x - n);
} for (int i = 2; i <= n; ++i) printf("%d\n", dis[i]);
return 0;
}
Splay
#include <iostream>
#include <cstdio>
#include <algorithm>
#include <cstring>
using namespace std;
const int N = 2e6 + 5;
int ch[N][2], fa[N], siz[N], cnt[N], v[N], root, nm = 0;
#define ls(x) ch[x][0]
#define rs(x) ch[x][1]
#define isRight(x) (x==ch[fa[x]][1])
inline int newNode(int x) { v[++nm] = x; cnt[nm] = siz[nm] = 1; return nm; }
inline void update(int x) { siz[x] = siz[ls(x)] + siz[rs(x)] + cnt[x]; }
inline void rotate(int x) {
int f = fa[x]; int ff = fa[f], c = isRight(x);
if (ff) ch[ff][isRight(f)] = x; fa[x] = ff;
if (ch[x][c ^ 1]) fa[ch[x][c ^ 1]] = f; ch[f][c] = ch[x][c ^ 1]; fa[f] = x; ch[x][c ^ 1] = f;
if (!fa[x]) root = x; update(f); update(x);
}
inline void splay(int x, int top = 0) {
while (fa[x] != top) { int ff = fa[fa[x]]; rotate(x); if (ff == top) break; rotate(x); } update(x);
}
inline int find(int x) {
if (!root) return -1;
int now = root, lst = 0, rt = 0;
while (now) {
if (v[now] == x) {
rt += siz[ls(now)];
break ;
} else if (v[now] > x) lst = now, now = ls(now);
else lst = now, rt += cnt[now] + siz[ls(now)], now = rs(now);
} splay(now ? now : lst); return rt;
}
inline void insert(int x) {
if (!root) { root = newNode(x); return ; }
find(x); if (v[root] == x) { ++cnt[root]; update(root); return ; }
int now = root; root = newNode(x); int c = v[now] > x;
ch[root][c] = now; fa[now] = root;
if (ch[now][c ^ 1]) fa[ch[now][c ^ 1]] = root;
ch[root][c ^ 1] = ch[now][c ^ 1]; ch[now][c ^ 1] = 0;
splay(now); return ;
}
inline void del(int x) {
find(x);
if (v[root] != x) return ;
if (cnt[root] > 1) { --cnt[root]; update(root); return ; }
if (!ch[root][1]) { root = ch[root][0]; fa[root] = 0; return ; }
if (!ch[root][0]) { root = ch[root][1]; fa[root] = 0; return ; }
int now = ch[root][1]; while (ch[now][0]) now = ch[now][0];
splay(now, root); ch[now][0] = ch[root][0];
if (ch[now][0]) fa[ch[now][0]] = now;
fa[root = now] = 0; update(root); return ;
}
inline int kth(int k) {
++k; int now = root;
while (now) {
if (k > siz[ls(now)] + cnt[now]) {
k -= siz[ls(now)] + cnt[now];
now = rs(now);
} else if (k <= siz[ls(now)]) now = ls(now);
else break;
} splay(now); return v[now];
}
inline int pre(int x) {
find(x);
if (v[root] < x) return v[root];
int now = ch[root][0];
while (rs(now)) now = rs(now);
return v[now];
}
inline int suf(int x) {
find(x);
if (v[root] > x) return v[root];
int now = ch[root][1];
while (ls(now)) now = ls(now);
return v[now];
}
inline int read() {
register int s = 0, f = 1;
register char ch = getchar();
while (!isdigit(ch)) f = (ch == '-' ? -1 : 1), ch = getchar();
while (isdigit(ch)) s = (s << 1) + (s << 3) + (ch & 15), ch = getchar();
return s * f;
}
int n, m, a;
int main() {
n = read(); m = read();
insert(0x7fffffff); insert(-0x7fffffff);
for (int i = 1; i <= n; ++i) insert(read());
int opt, x, la = 0, ans = 0;
while (m--) {
opt = read(); x = read() ^ la;
if (opt == 1) insert(x);
else if (opt == 2) del(x);
else if (opt == 3) ans ^= (la = find(x));
else if (opt == 4) ans ^= (la = kth(x));
else if (opt == 5) ans ^= (la = pre(x));
else if (opt == 6) ans ^= (la = suf(x));
} printf("%d\n", ans);
return 0;
}
文艺平衡树(区间翻转单点修改区间最大子段和)
就是把splay当成区间树用,把l - 1 splay到根,把r + 1 splay到根的右儿子,这样根的右儿子的左子树就代表这段区间了。每次翻转交换左右儿子即可。
另外由于提前加入了最小值,l - 1应对应l,r+1对应r+2,实现的时候注意细节。
#include <iostream>
#include <cstdio>
#include <algorithm>
#include <cstring>
using namespace std;
typedef long long ll;
const int N = 200005;
inline ll max_(ll a, ll b) {
return a > b ? a : b;
}
struct node {
ll ls, rs, sum, mx;
inline node() { ls = rs = sum = mx = 0; }
inline node(ll L, ll R, ll S, ll M) : ls(L), rs(R), sum(S), mx(M) { }
} nd[N], bs[N];
inline node merge(node a, node b) {
return node(max_(a.ls, a.sum + b.ls), max_(b.rs, a.rs + b.sum), a.sum + b.sum, max_(max_(a.mx, b.mx), a.rs + b.ls));
}
int ch[N][2], siz[N], cnt[N], tag[N], fa[N], v[N], nm = 0, root;
#define ls(x) ch[x][0]
#define rs(x) ch[x][1]
#define isRight(x) (x==ch[fa[x]][1])
inline void pushdown(int x) {
if (tag[x]) {
swap(nd[x].ls, nd[x].rs);
swap(ls(x), rs(x));
tag[ls(x)] ^= 1; tag[rs(x)] ^= 1;
tag[x] = 0;
}
}
inline void update(int x) {
siz[x] = siz[ls(x)] + siz[rs(x)] + cnt[x];
pushdown(ls(x)); pushdown(rs(x));
nd[x] = merge(merge(nd[ls(x)], bs[x]), nd[rs(x)]);
}
inline int newNode(int x, ll y) {
v[++nm] = x; siz[nm] = cnt[nm] = 1; bs[nm] = nd[nm] = node(max_(y, 0), max_(y, 0), y, max_(y, 0));
return nm;
}
inline void rotate(int x) {
pushdown(x);
int f = fa[x], ff = fa[fa[x]], c = isRight(x);
if (ff) ch[ff][isRight(f)] = x; fa[x] = ff;
ch[f][c] = ch[x][c ^ 1]; fa[ch[x][c ^ 1]] = f;
ch[x][c ^ 1] = f; fa[f] = x;
update(ff); update(f); update(x);
if (fa[x] == 0) root = x;
}
inline void splay(int x, int top = 0) {
while (fa[x] != top) {
int ff = fa[fa[x]], f = fa[x];
if (ff == top) { rotate(x); break; }
rotate(x); rotate(x);
} update(x);
}
inline int find(int x) {
if (!root) return -1;
int now = root, lst = 0, rt = 0;
while (now) {
if (v[now] == x) { rt += siz[ls(now)]; break; }
if (v[now] > x) now = ls(lst = now);
else rt += siz[ls(now)] + cnt[now], now = rs(lst = now);
} splay(now ? now : lst); return rt;
}
inline void insert(int x, ll val) {
if (!root) { root = newNode(x, val); return ; } find(x);
if (v[root] == x) { ++cnt[root]; update(root); return ; }
int now = root; root = newNode(x, val);
ch[root][0] = now; fa[fa[now] = root] = 0;
if (ch[now][1]) fa[ch[now][1]] = root; ch[root][1] = ch[now][1]; ch[now][1] = 0;
update(root); splay(now);
}
inline int kth(int k) {
int now = root;
while (now) {
pushdown(now);
if (k > siz[ls(now)] + 1) {
k -= siz[ls(now)] + 1;
now = rs(now);
} else if (k <= siz[ls(now)]) now = ls(now);
else return now;
} return now;
}
inline void print(int now) {
pushdown(now);
if (ch[now][0]) print(ch[now][0]);
if (v[now] != -0x7fffffff && v[now] != 0x7fffffff) cerr << bs[now].sum << ' ';
if (ch[now][1]) print(ch[now][1]);
}
inline void reverse(int l, int r) {
l = kth(l); r = kth(r + 2);
splay(l); splay(r, l);
tag[ch[ch[root][1]][0]] ^= 1;
}
inline ll query(int l, int r) {
l = kth(l); r = kth(r + 2);
splay(l); splay(r, l);
return nd[ch[ch[root][1]][0]].mx;
}
int n, m; ll q;
inline int read() {
register int s = 0, f = 1;
register char ch = getchar();
while (!isdigit(ch)) f = (ch == '-' ? -1 : 1), ch = getchar();
while (isdigit(ch)) s = (s << 1) + (s << 3) + (ch & 15), ch = getchar();
return s * f;
}
inline ll readll() {
register ll s = 0, f = 1;
register char ch = getchar();
while (!isdigit(ch)) f = (ch == '-' ? -1 : 1), ch = getchar();
while (isdigit(ch)) s = (s << 1) + (s << 3) + (ch & 15), ch = getchar();
return s * f;
}
inline char readchar() {
register char ch = getchar();
while (ch != 'Q' && ch != 'M' && ch != 'R') ch = getchar();
return ch;
}
int main() {
n = read(); m = read();
insert(-0x7fffffff, 0);
for (int i = 1; i <= n; ++i) {
q = readll();
insert(i, q);
} insert(0x7fffffff, 0);
int l, r; char opt;
while (m--) {
opt = readchar(); l = read(); r = read();
if (opt == 'R') reverse(l, r);//, print(root), cerr << endl;
else if (opt == 'Q') printf("%lld\n", query(l, r));
else if (opt == 'M') {
l = kth(l + 1); bs[l] = node(max_(r, 0), max_(r, 0), r, max_(r, 0));
update(l); splay(l);
}
} fclose(stdin); fclose(stdout);
return 0;
}
CDQ(陌上花开)
#include <iostream>
#include <cstdio>
#include <algorithm>
#include <cstring>
using namespace std;
inline int read() {
int s = 0, f = 1;
char ch = getchar();
while (!isdigit(ch)) f = ((ch == '-') ? -1 : 1), ch = getchar();
while (isdigit(ch)) s = (s << 1) + (s << 3) + (ch & 15), ch = getchar();
return s * f;
}
struct node {
int x, y, z, chs, *sum;
node() { x = y = z = 0x3f3f3f; chs = 0; }
inline bool operator == (const node &b) const {
return x == b.x && y == b.y && z == b.z;
}
}a[100005], b[100005], c[100005];
int sum[200005], ans[200005], n, k;
inline bool cmp(const node &a, const node &b) {
return a.x < b.x || a.x == b.x && a.y < b.y || a.x==b.x && a.y == b.y && a.z < b.z;
}
inline void cdq2(int l, int r) {
if (l >= r) return ;
int mid = l + r >> 1;
cdq2(l, mid); cdq2(mid + 1, r);
int i = l, j = mid + 1, pls = 0, tt = l;
while (tt <= r) {
if (i <= mid && (j > r || (b[i].z <= b[j].z))) {
c[tt] = b[i++];
pls += c[tt].chs;
} else { c[tt] = b[j++]; if (c[tt].chs != 1) *c[tt].sum += pls; }
++tt;
} for (i = l; i <= r; ++i) b[i] = c[i]; return ;
}
inline void cdq(int l, int r) {
if (l >= r) return ;
int mid = l + r >> 1;
cdq(l, mid); cdq(mid + 1, r);
int i = l, j = mid + 1, tt = l;
while (tt <= r) {
if (i <= mid && (j > r || (a[i].y <= a[j].y))) {
b[tt] = a[i++]; b[tt].chs = 1;
} else { b[tt] = a[j++]; b[tt].chs = 0; }
++tt;
} for (i = l; i <= r; ++i) a[i] = b[i];
cdq2(l, r); return ;
}
int main() {
n = read(); k = read();
for (int i = 1; i <= n; ++i) {
a[i].x = read(); a[i].y = read(); a[i].z = read();
a[i].sum = &sum[i]; sum[i] = 0;
}
sort(a + 1, a + n + 1, cmp);
for (int i = n - 1; i; --i)
if (a[i] == a[i + 1])
*a[i].sum = *a[i + 1].sum + 1;
cdq(1, n); for (int i = 1; i <= n; ++i) ++ans[sum[i]];
for (int i = 0; i < n; ++i) printf("%d\n", ans[i]);
return 0;
}
图论
没讲什么新的,大部分都是刷题,看提单吧。
最大流,费用流
#include <bits/stdc++.h>
using namespace std;
inline int min_(int a, int b) {
return a < b ? a : b;
}
template<int N> class max_flow {
private :
typedef long long ll;
struct Edge {
int to;
ll flow;
Edge(int T, ll F) : to(T), flow(F) {}
};
int s, t, dep[N], en;
vector<Edge> ed;
vector<int> e[N];
vector<int> :: iterator now[N];
inline bool bfs() {
memset(dep, -1, sizeof dep);
queue<int> q; q.push(s); dep[s] = 0; now[s] = e[s].begin();
while (!q.empty()) {
int x = q.front(); q.pop();
for (vector<int> :: iterator it = e[x].begin(); it != e[x].end(); ++it) {
int v = ed[*it].to; ll f = ed[*it].flow;
if (dep[v] == -1 && f > 0) {
q.push(v); now[v] = e[v].begin();
dep[v] = dep[x] + 1;
if (v == t) return 1;
}
}
} return dep[t] != -1;
}
inline ll dfs(int nw, ll fl) {
if (nw == t) return fl;
for (vector<int> :: iterator it = now[nw]; it != e[nw].end(); ++it) {
int v = ed[*it].to; ll f = ed[*it].flow;
now[nw] = it;
if (f != 0 && dep[v] == dep[nw] + 1) {
ll k = dfs(v, min_(fl, f));
if (k > 0) {
ed[*it].flow -= k;
ed[(*it) ^ 1].flow += k;
return k;
}
}
} return 0;
}
inline ll dinic() {
ll mxf, ans = 0;
while (bfs()) {
mxf = 0x3f3f3f3f;
while (mxf) {
mxf = dfs(s, 0x3f3f3f3f);
ans += mxf;
}
} return ans;
}
public :
max_flow() { en = -1; }
~max_flow() { }
inline void addedge(int from, int to, ll v) {
ed.push_back(Edge(to, v));
e[from].push_back(++en);
ed.push_back(Edge(from, 0));
e[to].push_back(++en);
}
inline ll maxflow(int s__, int t__) {
s = s__; t = t__;
return dinic();
}
};
max_flow<405>mf;
int n, m, s, t, u, v; long long w;
int main() {
scanf("%d%d%d%d", &n, &m, &s, &t);
while (m--) {
scanf("%d%d%lld", &u, &v, &w);
mf.addedge(u, v, w);
} printf("%lld", mf.maxflow(s, t));
return 0;
}
#include <bits/stdc++.h>
using namespace std;
inline int min_(int a, int b) {
return a < b ? a : b;
}
int n, m, s, t;
template <int N> class MinCost {
private :
struct Edge {
int from, to, flow, dis;
inline Edge(int n, int m, int s, int l) : from(n), to(m), flow(s), dis(l) {}
};
int s, t, pre[N], dis[N], last[N], flow[N], en;
bool vis[N];
vector<Edge> ed;
vector<int> e[N];
inline bool spfa() {
memset(vis, 0, sizeof vis);
memset(dis, 0x3f, sizeof dis);
pre[t] = -1;
memset(flow, 0x3f, sizeof flow);
dis[s] = 0;
vis[s] = 1;
deque<int> q;
q.push_front(s);
while (!q.empty()) {
int x = q.front();
q.pop_front();
vis[x] = 0;
for (vector<int> :: iterator it = e[x].begin(); it != e[x].end(); ++it) {
int v = ed[(*it)].to, fl = ed[(*it)].flow, d = ed[(*it)].dis;
if (fl > 0 && dis[v] > dis[x] + d) {
dis[v] = dis[x] + d;
flow[v] = min_(flow[x], fl);
pre[v] = x;
last[v] = (*it);
if (!vis[v]) {
vis[v] = 1;
if (!q.empty() && dis[q.front()] > dis[v]) q.push_front(v);
else q.push_back(v);
}
}
}
}
return pre[t] != -1;
}
public :
int mc, mf;
MinCost() { en = -1; }
~MinCost() {}
inline void addedge(int from, int to, int flow, int dis) {
ed.push_back(Edge(from, to, flow, dis));
e[from].push_back(++en);
ed.push_back(Edge(to, from, 0, -dis));
e[to].push_back(++en);
}
inline void MCMF(int __s, int __t) {
s = __s; t = __t;
mc = mf = 0;
while (spfa()) {
int now = t;
mf += flow[t];
mc += dis[t] * flow[t];
while (now != s) {
ed[last[now]].flow -= flow[t];
ed[last[now] ^ 1].flow += flow[t];
now = pre[now];
}
}
}
};
MinCost<100005> mc;
int main() {
scanf("%d%d%d%d", &n, &m, &s, &t);
int u, v, w, d;
while (m--) {
scanf("%d%d%d%d", &u, &v, &w, &d);
mc.addedge(u, v, w, d);
}
mc.MCMF(s, t);
printf("%d %d", mc.mf, mc.mc);
return 0;
}
匈牙利:
#include <iostream>
#include <cstdio>
#include <algorithm>
#include <cstring>
#include <vector>
#include <queue>
using namespace std;
inline int read() {
int s = 0;
char ch = getchar();
while (!isdigit(ch)) ch = getchar();
while (isdigit(ch)) s = (s << 1) + (s << 3) + (ch & 15), ch = getchar();
return s;
}
struct Edge {
int from, to, flow;
inline Edge(int n, int m, int l) : from(n), to(m), flow(l) {}
};
int n, m, s, t, en = -1, dep[10005];
vector<Edge> ed;
vector<int>e[10005];
inline int min_(int a, int b) {
return a < b ? a : b;
}
inline void addedge(int from, int to, int v) {
ed.push_back(Edge(from, to, v));
e[from].push_back(++en);
ed.push_back(Edge(to, from, 0));
e[to].push_back(++en);
}
inline bool bfs(int s) {
queue<int> q; memset(dep, -1, sizeof(dep)); dep[s] = 1; q.push(s);
while (!q.empty()) {
int x = q.front(); q.pop();
for (vector<int> :: iterator it = e[x].begin(); it != e[x].end(); ++it) {
int v = ed[(*it)].to, fl = ed[(*it)].flow;
if (fl > 0 && dep[v] == -1) dep[v] = dep[x] + 1, q.push(v);
}
} return dep[t] != -1;
}
inline long long dfs(int now, long long fl) {
if (now == t) return fl;
for (vector<int> :: iterator it = e[now].begin(); it != e[now].end(); ++it) {
int v = ed[(*it)].to, f = ed[(*it)].flow;
if (dep[now] + 1 == dep[v] && f != 0) {
int k = dfs(v, min_(fl, f));
if (k > 0) { ed[(*it)].flow -= k; ed[(*it) ^ 1].flow += k; return k; }
}
} return 0;
}
int main() {
n = read(); m = read(); int e = read(); s = 1; t = n + m + 2;
while (e--) {
int u = read(); int v = read();
if (v > m || u > n) continue;
addedge(u + 1, v + n + 1, 1);
} for (int i = 1; i <= n; ++i) addedge(s, i + 1, 1);
for (int i = 1; i <= m; ++i) addedge(i + n + 1, t, 1);
long long mxf = 0, ans = 0;
while (bfs(s)) {
mxf = 0x3f3f3f3f;
while (mxf) {
mxf = dfs(s, 0x3f3f3f3f);
ans += mxf;
}
} printf("%lld", ans); return 0;
}
kruskal重构树:
用它的题目有主要特征:从某个点开始只能走权值小于(或大于)k的边...
以P4197 Peaks 为例子,每次问你某个点开始只能走权值小于等于x的边,到达的点中点权第k大的点。
首先要考虑把那些点弄出来。能走到,表明两点间所有路径上边权的最大值得最小值<=x。通过这一点想到了什么?最小生成树。
开始构建ksuskal重构树。重构树的叶节点相当于原图中的点。我们首先进行ksuskal算法。假设当前被选中的边为(u,v),f[x]表示点x在重构树中当前所在连通块的根,则新建一个节点。另它的两个儿子分别为f[u]和f[v],它的点权为这条边的边权。这棵树有和性质?
首先,深度越浅的点边权越大(因为kruskal时他们在后边才加入的),并且重构树中两点lca的权值相当于这两点之间所有路径上边权的最大值的最小值!考虑kruskal算法的原理,这个lCA的边权是这两个点第一次连通时连上的边,连得时间靠后,时当时最小生成树上最大的边,且此时在最小生成树上它们两个已经连通了。根据最小生成树的性质,最小生成树上两点路径中最大的边等于原图中两点间所有路径上的边权的最大值的最小值。
那么根据上面的性质,不难想到以下算法。假设要求点x走边权不超过v的边能到的所有点,我们在重构树上以x为起点向上倍增,直到倍增到深度最小的权值小于v的点。此时这个点的子树的所有叶节点就对应了原图中点x走边权不超过v的边能到的所有点。
这个找出来什么都好办。直接树上的dfs序+主席树/划分树维护即可。
#include <iostream>
#include <cstdio>
#include <algorithm>
#include <cstring>
#include <vector>
#include <map>
using namespace std;
int n, m, q, t, h[200005], nxt, u, v, w, cnt = 0;
map<int, int> mp, ts;
int sorted[200005], num[21][200005], val[21][200005];
//MST
int f[210005];
inline int find(int x) {
return f[x] == x ? x : f[x] = find(f[x]);
}
//原图
struct edge {
int from, to, val;
}ed[500005];
inline bool cmp(edge a, edge b) {
return a.val < b.val;
}
//kruskal重构树
int rt;
struct nodde {
int fa[20], val, siz, l;
nodde() {
siz = 0; l = 0x3f3f3f3f;
}
vector<int> nxt;
}nd[210005];
inline void addedge(int from, int to) {
nd[from].nxt.push_back(to);
}
//kruskal
inline int min_(int a, int b) {
return a < b ? a : b;
}
inline void kruskal() {
cnt = n; //memset(f, 0, sizeof f);
// cout << "edges in MST : " << endl;
sort(ed + 1, ed + m + 1, cmp);
for (int i = 1; i < 2100005; ++i) f[i] = i;
int edgenum = 0;
for (int i = 1; i <= m; ++i) {
int x = find(ed[i].from), y = find(ed[i].to);
if (x == y) continue;
// cout << ed[i].from << ' ' << ed[i].to << ' ' << ed[i].val << endl;
++edgenum;
nd[x].fa[0] = ++cnt;
nd[y].fa[0] = cnt;
f[x] = f[y] = cnt;
nd[cnt].val = ed[i].val;
addedge(cnt, x); addedge(cnt, y);
if (edgenum == n - 1) {
return ;
}
}
}
//dfsxv
int dfn[200005], nm = 0;
inline void dfs(int now) {
// cout << "fa = " << nd[now].fa[0] << endl;
for (int i = 1; i < 20; ++i)
nd[now].fa[i] = nd[nd[now].fa[i - 1]].fa[i - 1];
int flag = 1;
if (now <= n) dfn[nd[now].l = ++nm] = now;
for (vector<int> :: iterator it = nd[now].nxt.begin(); it != nd[now].nxt.end(); ++it) {
if (*it == nd[now].fa[0]) continue;
dfs(*it);nd[now].siz += nd[*it].siz; flag = 0;
nd[now].l = min_(nd[*it].l, nd[now].l);
} nd[now].siz += flag; //cout << "siz[" << now << "] = " << nd[now].siz << endl;
}
inline int findfa(int now, int x) {
// cout << "jump! " << now << ' ' << x << "\n";
int u = now;
for (int i = 19; i >= 0; --i) {
if (nd[nd[u].fa[i]].val > x) continue;
u = nd[u].fa[i];//cout << u << endl;
} return u <= n ? -1 : u;
}
void build(int l, int r, int ceng) {
if (l == r) return ;
int mid = l + r >> 1, isame = mid - l + 1;
for (int i = l; i <= r; i++) if (val[ceng][i] < sorted[mid]) isame--;
int ln = l, rn = mid + 1;
for (int i = l; i <= r; i++) {
if (i == l) num[ceng][i] = 0;
else num[ceng][i] = num[ceng][i-1];
if (val[ceng][i] < sorted[mid] || val[ceng][i] == sorted[mid] && isame > 0) {
val[ceng + 1][ln++] = val[ceng][i];
num[ceng][i]++;
if(val[ceng][i] == sorted[mid]) isame--;
} else val[ceng+1][rn++]=val[ceng][i];
} build(l, mid, ceng + 1); build(mid + 1, r, ceng + 1);
}
int look(int ceng, int sl, int sr, int l, int r, int k) {
if (sl == sr) return val[ceng][sl]; int ly;
if (l == sl) ly = 0; else ly = num[ceng][l - 1];
int tolef = num[ceng][r] - ly;
if (tolef >= k) return look(ceng + 1, sl, sl + sr >> 1, sl + ly, sl + num[ceng][r] - 1, k);
int lr = (sl + sr >> 1) + 1 + (l - sl - ly);
return look(ceng +1, (sl + sr) / 2 + 1, sr, lr, lr + r - l + 1 - tolef - 1, k - tolef);
}
int qans(int l, int r, int k) { return look(0, 1, n, l, r, k); }
int main() {
// freopen("sample2.in", "r", stdin);
// freopen("ans.ans", "w", stdout);
scanf("%d%d%d", &n, &m, &q);
for (int i = 1; i <= n; ++i) scanf("%d", &h[i]);
for (int i = 1; i <= m; ++i)
scanf("%d%d%d", &ed[i].from, &ed[i].to, &ed[i].val);
nd[0].val = 0x3f3f3f3f; kruskal(); rt = find(1); dfs(rt);
for (int i = 1; i <= n; ++i) sorted[i] = val[0][i] = dfn[i] = h[dfn[i]];
sort(sorted + 1, sorted + n + 1); build(1, n, 0);
int lastans = 0;
while (q--) {
scanf("%d%d%d", &u, &v, &w);
u = findfa(u, v);//puts("fuck");
if (u == -1) {
puts("-1");
lastans = 0;
continue;
}
if (nd[u].siz < w) {
puts("-1");
lastans = 0;
continue;
}
printf("%d\n", qans(nd[u].l, nd[u].l + nd[u].siz - 1, nd[u].siz - w + 1));
}
return 0;
}
模板题还有[NOI2018]规程(卡SPFA)
严格次小生成树
原理就是可证明它只有一条边与最小生成树不同。枚举那条边即可。
#include <iostream>
#include <cstdio>
#include <algorithm>
#include <cstring>
using namespace std;
const int N = 1e5 + 5;
typedef long long ll;
#define int long long
int en = 0;
struct edge {
int head, to, nxt, val;
} ed[N << 1];
inline void addedge(int from, int to, int val) {
ed[++en].to = to; ed[en].val = val; ed[en].nxt = ed[from].head; ed[from].head = en;
}
struct Edge {
int from, to, val, flag;
} e[N * 3];
inline bool cmp(Edge a, Edge b) {
return a.val < b.val;
}
int n, m, mn = 1ll * 1e9 * 1e9;
ll ans = 0;
inline int read() {
register int s = 0, f = 1;
register char ch = getchar();
while (!isdigit(ch)) f = (ch == '-' ? -1 : 1), ch = getchar();
while (isdigit(ch)) s = (s << 1) + (s << 3) + (ch & 15), ch = getchar();
return s * f;
}
int f[N];
inline int find(int x) {
return x == f[x] ? x : f[x] = find(f[x]);
}
inline void init() {
for (int i = 1; i <= n; ++i) f[i] = i;
}
inline void kruskal() {
sort(e + 1, e + m + 1, cmp);
for (int i = 1; i <= m; ++i) {
int u = e[i].from, v = e[i].to, w = e[i].val;
int fu = find(u), fv = find(v);
if (fu == fv) continue;e[i].flag = 1;
f[fu] = fv; ans += w;
addedge(u, v, w); addedge(v, u, w);
}
}
int dep[N], fa[N][20], mx[N][20], mx2[N][20];
inline int max_(int a, int b) {
return a > b ? a : b;
}
inline int min_(int a, int b) {
return a < b ? a : b;
}
inline void dfs(int now) {
// cerr <<"DFS : " << now << endl;
for (int i = 1; i < 20; ++i) {
fa[now][i] = fa[fa[now][i - 1]][i - 1];
mx[now][i] = max_(mx[now][i - 1], mx[fa[now][i - 1]][i - 1]);
mx2[now][i] = max_(mx2[now][i - 1], mx2[fa[now][i - 1]][i - 1]);
if (mx[now][i - 1] != mx[fa[now][i - 1]][i - 1])
mx2[now][i] = max_(mx2[now][i], min_(mx[now][i - 1], mx[fa[now][i - 1]][i - 1]));
}
for (int i = ed[now].head; i; i = ed[i].nxt) {
int v = ed[i].to; if (v == fa[now][0]) continue;
dep[v] = dep[now] + 1; fa[v][0] = now;
mx[v][0] = ed[i].val; mx2[v][0] = -1;
dfs(v);
}
}
inline int LCA(int a, int b) {
// cerr << "LCAING : " << a << ' ' << b << endl;
// cerr << "DEP : " << dep[a] << ' ' << dep[b] << endl;
int u = a, v = b;
if (dep[u] < dep[v]) swap(u, v);
int d = dep[u] - dep[v];
for (int i = 0; i < 20; ++i)
if ((d >> i) & 1) u = fa[u][i];
// cerr << "SH : " << u << ' ' << v << endl << endl;
if (u == v) return u;
for (int i = 19; ~i; --i)
if (fa[u][i] != fa[v][i])
u = fa[u][i], v = fa[v][i];
return fa[u][0];
}
inline int getmax(int a, int d) {
int ret = 0;
for (int i = 19; ~i; --i)
if (dep[fa[a][i]] >= d) {
ret = max_(ret, mx[a][i]);
a = fa[a][i];
}
return ret;
}
inline int getmax2(int a, int d) {
int ret = 0, ret2 = -1;
for (int i = 19; ~i; --i)
if (dep[fa[a][i]] >= d) {
if (ret == mx[a][i]) ret2 = max_(ret2, mx2[a][i]);
ret2 = max_(min_(ret, mx[a][i]), ret2);
ret = max_(ret, mx[a][i]);
a = fa[a][i];
}
return ret2;
}
signed main() {
n = read(); m = read(); init();
for (int i = 1; i <= m; ++i) {
e[i].from = read(); e[i].to = read(); e[i].val = read();
} kruskal();// cerr << endl;
dfs(1);
for (int i = 1; i <= m; ++i) {
if (e[i].flag) continue;
int u = e[i].from, v = e[i].to, w = e[i].val;
int L = LCA(u, v);//cerr << u << ' ' << v << ' ' << L << endl;
int ma = getmax(u, dep[L]), mb = getmax(v, dep[L] + 1), ma2 = getmax2(u, dep[L]), mb2 = getmax2(v, dep[L] + 1);
int mx = max_(ma, mb);
if (mx < w) mn = min_(mn, w - mx);
else {
int mx2 = max_(max_(ma2, mb2), min_(ma, mb));
if (mx2 == w) continue;
mn = min_(mn, w - mx2);
}
} printf("%lld", ans + mn);
return 0;
}
搜索
IDA*(魔法猪学院)
#include <iostream>
#include <cstdio>
#include <algorithm>
#include <cstring>
#include <queue>
using namespace std;
int n, m;
double e;
struct edge {
int head, to, nxt;
double val;
}ed[200005], ied[200005];
int en = 0, ien = 0;
inline void addedge(int from, int to, double v) {
ed[++en].to = to; ed[en].val = v; ed[en].nxt = ed[from].head; ed[from].head = en;
}
inline void addedge_(int from, int to, double v) {
ied[++ien].to = to; ied[ien].val = v; ied[ien].nxt = ied[from].head; ied[from].head = ien;
}
double h[20005];
bool vis[20005];
inline void geth() {
for (int i = 0; i <= n; ++i) h[i] = 1e7 + 1;
h[n] = 0;
for (int i = 1; i <= n; ++i) {
int x = 0;
for (int j = 1; j <= n; ++j)
if (!vis[j] && h[j] < h[x]) x = j;
if (x == 0) return ;
vis[x] = 1;
for (int j = ied[x].head; j; j = ied[j].nxt) {
int v = ied[j].to;
if (h[v] > h[x] + ied[j].val) h[v] = h[x] + ied[j].val;
}
}
}
struct node {
int x; double g;
inline node(int X, double G) : x(X), g(G) {}
inline bool operator < (const node& b) const { return g + h[x] < b.g + h[b.x]; }
inline bool operator > (const node& b) const { return g + h[x] > b.g + h[b.x]; }
};
priority_queue<node, vector<node>, greater<node> > pq;
int got[20005], ans = 0;
inline void SPFA(int limited) {
pq.push(node(1, 0));
while (!pq.empty() && pq.size() <= 3990000) {
int x = pq.top().x;
double dis = pq.top().g;
++got[x]; pq.pop();
if (dis > e) return ;
if (x == n) { ++ans; e -= dis; continue; }
if (got[x] > limited) continue;
for (int i = ed[x].head; i; i = ed[i].nxt)
pq.push(node(ed[i].to, dis + ed[i].val));
}
}
int main() {
scanf("%d%d%lf", &n, &m, &e);
for (int i = 1; i <= m; ++i) {
int u, v; double w;
scanf("%d%d%lf", &u, &v, &w);
addedge(u, v, w); addedge_(v, u, w);
} geth(); SPFA(e / h[1]);
printf("%d", ans);
return 0;
}