ACM模板【更新中】
\(Author: zhl\)
\(LastUpdate: 2020-10-14\)
0.其他
// ..,,***///////*,..
// ./#((((((((((((((((((((((//,.
// ,/((%&###(((((((((((((((((((((((((/,.
// .*((##&&%%%&&%%#(((((((((((((((((((((((##,
// ....,,,,,,**(##((((((((##%&%%#((((((((((((((((((%&((,.
// ..,,,,,,,,,,,,,,,*/(((((((((((#%%&%%%%%&&&&&&&&&&&&%/((*.
// .,,,,,,,,,,,,,,,,,,,,,*/(((((((((((%&%(((((((((((((#&&#((((.
// .,,*#%&%(,,,,*(%&%/,,,,,,/((((((((((%&%(((((((((((((((%&%((((,.,....
// .,,*#&&%(*,,,*#%&&#,,,,,,,*(((((((((%&%((((((((((((((((#&%#((#/,,,,.
// .,,,,**,*,,,,,,**,,,,,,,,,,/##((((((%&#(((((((((((((((((#&%%&%(,,.
// .,,,,,,(%#(##%%/,,,,,,,,,,,,(%%%#(((%&#(((((((((((((((##%&&%#((*.
// .,,,,,,,,****,,,,,,,,,,,,,,,*/(%&%#(%&#((((((((((#%&&&%%#(((((/,
// ,,,,,,,,,,,,,,,,,,,,,,,,,,*/(((#%%&&&&&&&&%%%%##(((((((((/*,.
// ..,,,,,,,,,,,,,,,,,,,,,,,,*(((((((((((((((((((((((/**,,,,,,.
// .,,,,,,,,,,,,,,,,,,,,,,*/((((((((((/(//***,,..,,,,,,,,,,,.
// .,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,..... .,,,,,,,,,,.
// .,,,,,,,,,,.. .,,,,,,,,,,,. ...........
// .,,,,,,,,,,. .,,,,,,,,,,,,
// ....... ..,,,,,,,,,,,.
龟龟
// #@@# . .,,,.
// /@@@@@& @@@@@&
// /@@@@ @@@@
// /% *(#%,,,,&%, .@@.
// ( ./* ,*. %
// ( * , .
// @@ ,. ., @@ @@@@,
// &@@@@. ., ,. . /@@@@@&&&&@@@@@.
// @@@@@@@@@@@@#* .,%@@@@@@@@@@@@@@@@@
// @@@@@@@, @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
// . @@@@@@ @@@@@@@@@@@@@@@@@@@@@@@@@@%%%&%&&@&%@&
// . .//, &@@@@ @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@#%%&&
// (/,,, * @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@&%%%@@/
// ((*//,,* #/&# @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@#%%@@&@@@@@*
// #((###%%%%%#( .&@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@%&&@@%/@@@@@@
// %####%%%%%%%%%%,* @@@@@@@@@# @@@@@@@@@&&&@@
// ####%%%%%%%%%%/ @@@@@@@, @@@@@@@@@@&&@&
// (##((#%%%%%%%%# &@@. @@&...&%@@@@@&&
// /(((##%%%%%%%% @%,
// /((##%%%%%%%%
// ,#%&%#
快读
template<typename T>
void read(T& x)//输入
{
x = 0;
int f = 1;
char ch;
if ((ch = getchar()) == '-') f = -f;
else x = x * 10 + ch - '0';
while ((ch = getchar()) >= '0' && ch <= '9')
x = x * 10 + ch - '0';
x *= f;
}
template<typename T>
void print(T x) {
if (x < 0) {
putchar('-');
x = -x;
}
if (x > 9)
print(x / 10);
putchar(x % 10 + '0');
}
__int128可以直接使用
1.数学
gcd
辗转相除法
\(gcd(a,b) = gcd(b,a \% b)\)
int gcd(int a,int b){
return (a % b == 0) ? b : gcd(b, a % b);
}
//或者直接用__gcd()
快速幂
int qpow(int a, int p) {
int ans = 1;
while (p) {
if (p & 1) ans = ans * a % mod;
a = a * a % mod;
p >>= 1;
}
return ans;
}
逆元
费马小定理
\(inv(a) = a^{mod-2}\)
递推打表
$ inv[i] = (mod\ -\ mod/i)*inv\ [mod%i\ ]%mod $
void get_inv(){
inv[1] = 1;
for(int i = 2;i < maxn;i++){
inv[i] = (mod - mod / i) * inv[mod % i] % mod;
}
}
欧拉筛(线筛)
void get_phi(int n) {
for (int i = 2; i <= n; i++) {
if (!book[i]) {
prime[++cnt] = i;
phi[i] = i - 1; // i是素数
}
for (int j = 1; j <= cnt; j++) {
if (i * prime[j] > n) break;
book[i * prime[j]] = 1;
phi[i * prime[j]] = phi[i] * phi[prime[j]];
if (!i % prime[j]) {
phi[i * prime[j]] = phi[i] * prime[j];
break;
}
}
}
}
整除分块
for(int l = 1,r = 0;l <= n;l = r + 1){
r = n/(n/l);
//sum += (n/l)*(r-l+1);
}
扩展欧几里得
首先看看二元一次不定方程
ax + by = c
①若gcd(a,b) | c,则方程有整数解
此时,方程左右同时处以gcd(a,b)
则此时a,b互素
此时只需要解方程
ax + by = 1即可,因为将解翻倍即可得到右边不为1的其他解,(a,b此时互素)
扩展欧几里得,将方程 ax + by == gcd(a,b) 求解,并顺便解除gcd(a,b)
void ex_gcd(int a,int b,int &gcd,int &x,int &y){
if(b == 0){
x = 1;
y = 0;
gcd = a;
}
else{
ex_gcd(b,a % b,gcd,y,x); //a,b交换,x,y跟着交换,大概应该是这个意思
y -= x*(a/b);
}
}
ex_gcd还可以求逆元;
**ex_gcd(a,m,x,y), 则 x = inv(a) (mod m) **
此时m可以不为素数。
中国剩余定理
问题
$x\equiv a_1 ( mod\ m_1) $
\(x \equiv a_2(mod\ m_2)\)
\(......\)
\(x \equiv a_k(mod\ m_k)\)
其中 \(m\) 两两互素
int CRT(){
int res = 0,M = 1;
int x,y,gcd;
for(int i = 1;i <= k;i++){
M *= m[i];
}
for(int i = 1;i <= k;i++){
int tmp = M / m[i];
ex_gcd(tp,m[i],gcd,x,y);
x = (x % m[i] + m[i]) % m[i];
res = (res + tmp * a[i] * x) % M;
}
return (res + M) % M;
}
扩展中国剩余定理
取消了两两互素的限制
int EX_CRT(){
int x,y,k,gcd;
int M = m[1];int res = a[1];
for(int i = 2;i <= k;i++){
int a = M,b = m[i], c = ((a[i] - res) % b + b) % b;
ex_gcd(a,b,gcd,x,y);
int tmp = b / gcd;
if(c % gcd != 0) return -1; //方程无解
x = mul(x,c/gcd,tmp); //因为系数不为1
res += x*M;
M *= tmp;
res = (res % M + M) % M;
}
return (res % M + M) % M;
}
欧拉降幂
求欧拉函数
欧拉筛
直接计算
int euler_phi(int n) {
int m = (int)sqrt(n + 0.5);
int ans = n;
for (int i = 2; i <= m; ++i) {
if (n % i == 0) {
ans = ans / i *(i - 1);
while (n % i == 0) n /= i;
}
}
if (n > 1) ans = ans / n *(n - 1);
return ans;
}
杜教筛
要求的东西 : \(\sum f(i)\)
设
记 \(S(n) = \sum_{i=1}^{n}f(i)\)
这一步的两个 \(\sum\) 的位置变换可以自己在纸上举了例子体会一下
#include<bits/stdc++.h>
#include<unordered_map>
using namespace std;
template<typename T>void read(T& x) {
x = 0;
int p = 1;
char c = getchar();
while (!isdigit(c)) { if (c == '-')p = -1, c = getchar(); }
while (isdigit(c)) { x = (x << 1) + (x << 3) + (c - 48); c = getchar(); }
x *= p;
}
template<typename T>void write(T x) {
if (x < 0) putchar('-'), x = -x;
if (x > 9) write(x / 10);
putchar(x % 10 + '0');
}
typedef long long ll;
const int maxn = 6e6 + 10;
int mu[maxn];
ll phi[maxn];
int sum_mu[maxn];
ll sum_phi[maxn];
int vis[maxn];
int prime[maxn], cnt;
unordered_map<int, ll>MU, PHI;
void get(int N) {
phi[1] = mu[1] = 1;
for (int i = 2; i <= N; i++) {
if (!vis[i]) {
prime[++cnt] = i;
mu[i] = -1;
phi[i] = i - 1;
}
for (int j = 1; j <= cnt && prime[j] * i <= N; j++) {
vis[prime[j] * i] = 1;
if (i % prime[j] == 0) {
phi[i * prime[j]] = phi[i] * prime[j];
break;
}
else {
mu[i * prime[j]] = mu[i] * -1;
phi[i * prime[j]] = phi[i] * (prime[j] - 1);
}
}
}
for (int i = 1; i <= N; i++) {
sum_phi[i] = sum_phi[i - 1] + phi[i];
sum_mu[i] = sum_mu[i - 1] + mu[i];
}
}
ll djs_mu(ll x) {
if (x <= maxn - 10)return sum_mu[x];
if (MU.count(x) != 0)return MU[x];
int ans = 1;
for (int l = 2, r;l <= x; l = r + 1) {
r = x / (x / l);
ans -= (r - l + 1) * djs_mu(x / l);
}
return MU[x] = ans;
}
ll djs_phi(ll x) {
if (x <= maxn - 10)return sum_phi[x];
if (PHI.count(x) != 0)return PHI[x];
ll ans = x * (x + 1) / 2;
for (ll l = 2, r; l <= x; l = r + 1) {
r = x / (x / l);
ans -= (r - l + 1) * djs_phi(x / l);
}
return PHI[x] = ans;
}
int main() {
int t, n;
read(t);
get(maxn - 10);
while (t--) {
read(n);
write(djs_phi(n)); putchar(' ');
write(djs_mu(n)); puts("");
}
}
min25筛
一种低于线性复杂度的求积性函数前缀和的筛法。
适用条件:
- \(f(p)\) 是多项式
- \(f(p^k)\) 便于计算
思想:
分为两个部分,第一部分是所有素数,第二部分是所有的合数
第一部分
搞来一个这样的函数 \(g(n,j)\)
所有的素数加上满足\(minp(i) > P_j\) 的所有 \(i\)
\([1-n]\) 中所有质数的 \(k\) 次方之和就是 \(g(n,x)\) ,\(P_x\) 是最后一个小于等于
\(\sqrt n\) 的质数
考虑 \(g(n,j)\) 的转移
这个东西自己在纸上写一些体会一下,注意 \(P_j\) 筛去的第一个数是 \(P_j^2\) , 第二个数不是 \(P_j^2+ P_j\)
第二部分
设
可以把 \(S(n,x)\) 也分成两部分,一部分是所有大于 \(P_x\) 的质数,另一部分是最小质因数大于 \(P_x\) 的合数,枚举最小质因子
当 \(e = 1\) 的时候, \(P_k\) 在前面枚举过了,不等于 \(1\) 时,需要加上 \(P_k^e\)
存下所有可能的 \(\lfloor\dfrac n x \rfloor\) , 做一个映射
#include<bits/stdc++.h>
using namespace std;
#define ll long long
const ll mod = 1e9 + 7, inv6 = 166666668, inv2 = 500000004;
const int maxn = 1e6 + 10;
ll n, sqr;
ll prime[maxn], cnt, vis[maxn];
ll sp1[maxn], sp2[maxn];//sp1 p的前缀和,sp2 p^2的前缀和
ll w[maxn], tot;
ll g1[maxn], g2[maxn], ind1[maxn], ind2[maxn];
void get(int maxn) {
for (int i = 2; i <= maxn; i++) {
if (!vis[i]) {
prime[++cnt] = i;
sp1[cnt] = (sp1[cnt - 1] + i) % mod;
sp2[cnt] = (sp2[cnt - 1] + 1ll * i * i) % mod;
}
for (int j = 1; j <= cnt && prime[j] * i <= maxn; j++) {
vis[prime[j] * i] = 1;
if (i % prime[j] == 0)break;
}
}
}
ll S(ll x, int y){
if (prime[y] >= x)return 0;
ll k = x <= sqr ? ind1[x] : ind2[n / x];
ll ans = (g2[k] - g1[k] + mod - (sp2[y] - sp1[y]) + mod) % mod;
for (int i = y + 1; i <= cnt && prime[i] * prime[i] <= x; i++)
{
ll pe = prime[i];
for (int e = 1; pe <= x; e++, pe = pe * prime[i])
{
ll xx = pe % mod;
ans = (ans + xx * (xx - 1) % mod * (S(x / pe, i) + (e != 1))) % mod;
}
}
return ans % mod;
}
int main() {
scanf("%lld", &n);
sqr = sqrt(n);
get(sqr);
for (ll l = 1, r; l <= n; l = r + 1) {
r = n / (n / l);
w[++tot] = n / l;
ll k = w[tot] % mod;
g1[tot] = (k * (k + 1) % mod * inv2 - 1 + mod) % mod;
g2[tot] = (k * (k + 1) % mod * (2 * k + 1) %mod * inv6 % mod + mod - 1) % mod;
if (w[tot] <= sqr)ind1[n / l] = tot;
else ind2[n / (n / l)] = tot;
}
for (int i = 1; i <= cnt; i++) {
//g(n,j) 滚第一维
for (int j = 1; j <= tot && prime[i] * prime[i] <= w[j]; j++) {
ll k = w[j] / prime[i] <= sqr ? ind1[w[j] / prime[i]] : ind2[n / (w[j] / prime[i])];
g1[j] -= prime[i] * (g1[k] - sp1[i - 1] + mod) % mod;
g2[j] -= prime[i] * prime[i] % mod * (g2[k] - sp2[i - 1] + mod) % mod;
g1[j] %= mod; g2[j] %= mod;
if (g1[j] < 0)g1[j] += mod;
if (g2[j] < 0)g2[j] += mod;
}
}
printf("%lld\n", (S(n, 0) + 1) % mod); //f(1) = 1
}
常见生成函数
有三种物品,分别有 3 ,2, 3个,问拿四个的方案数
f[i][j]
表示当前第 i
个位置,已经选了 j
个物品的方案数
f[0][0] = 1;
for(int i = 1;i <= 3;i++){
for(int j = 0;j <= 8;j++){//总共要选j个
for(int k = 0;k <= j;k++){//已经选了k个
if(j - k <= v[i])//此时要选j-k个
f[i][j] += f[i-1][k];
}
}
}
第一种物品的生成函数 \(G_1(x) = 1 + x + x^2 + x ^ 3\)
\(G_2(x) = 1 + x + x^2\) , $G_3 = 1 + x + x^2 + x^3 $
\(G_1(x)*G_2(x)*G_3(x)\) ,中 \(x^4\) 的系数就是答案
上述代码其实就是在求多项式乘法的系数
指数生成函数
将上述问题改成排列方案hdu1521
构造出
\(G_1(x) = 1+\frac{x^1}{1} + \frac{x^2}{2!} + \frac{x^3}{3!}\)
\(G_2(x) = 1 + \frac{x^1}{1} + \frac{x^2}{2}\)
\(G_3(x) = 1 + \frac{x^1}{1} + \frac{x^2}{2!} + \frac{x^3}{3!}\)
答案就是 \(x^4\) 的系数乘上 \(4!\) , \(\frac{35}{12} * 4! = 70\)
(1-x)^-1 型
广义二项式定理
至多为 \(k\) 就是 \(\dfrac {1-x^{k+1}} {1-x}\)
\(k\) 的倍数就是 \(\dfrac 1 {1-x^k}\)
最后的结果是 \(\dfrac 1 {(1-x)^5}\) , 带入广义二项式定理, 答案是 \(C_n^4\)
\(py\) 草不过去, \(OI\)👴直呼 人生苦短我用 \(ruby\)
e^x 型
五边形数定理
整数拆分
五边形数定理
这里的欧拉函数 \(\phi(q)\) 是复变函数
五边形数定理描述了欧拉函数的展开式特性
欧拉函数展开后,有些次方项被消去,只留下次方项为1, 2, 5, 7, 12, ...的项次,留下来的次方恰为广义五边形数。
拆分函数 \(p(n)\)
根据欧拉发现的五边形数定理,描述欧拉函\(ϕ(x)\)如下:
那么我们就很容易发现欧拉函数的导数是分割函数的母函数:
那么在考虑\(x^n\)项的系数的时候,在\(n>0\)的情况下,系数都为0,那么就能得到
这个的时间复杂度可以在O(nlogn)时间内解决。
暂时不知道怎么 \(nlogn\) 搞,好像要多项式求逆啥的
待补
但是可以 \(O(n\sqrt n)\) 搞
#include <iostream>
using namespace std;
typedef long long ll;
const ll N = 500005, mod = 998244353;
ll f[N], g[N], dp[N];
int n;
int main() {
scanf("%d", &n);
--n;
for (ll i = 1; i <= n; ++i) f[i] = i * (3*i - 1) / 2;
for (ll i = 1; i <= n; ++i) g[i] = i * (3*i + 1) / 2;
dp[0] = 1;
for (int i = 1; i <= n; ++i) {
for (int j = 1; f[j] <= i; ++j) {
if (j & 1) {
dp[i] += dp[i - f[j]];
if (g[j] <= i) dp[i] += dp[i - g[j]];
} else {
dp[i] -= dp[i - f[j]];
if (g[j] <= i) dp[i] -= dp[i - g[j]];
}
}
dp[i] %= mod;
if (dp[i] < 0) dp[i] += mod;
}
printf("%lld\n", dp[n]);
return 0;
}
FFT
由 系数表示法 转换为 点值表示法
记
设
则
带入 \(x = \omega_n^k\)
带入 $x = \omega_n^{k+\frac n2} $
也就是说如果知道了 $A_1(x),A_2(x) $ 分别在 \(\omega_{\frac n2}^0\) , \(\omega_{\frac n2}^1\) , \(\omega_{\frac n2}^2\) ,...,\(\omega_{\frac n2}^{\frac n2 -1}\) 的取值,
就可以 \(O(n)\) 的求出 \(A(x)\)
void fft(cp *a,int n,int inv)//inv是取共轭复数的符号
{
if (n==1)return;
int mid=n/2;
static cp b[MAXN];
for(int i = 0;i < mid;i++)b[i]=a[i*2],b[i+mid]=a[i*2+1];
for(int i = 0;i < n;i++)a[i]=b[i];
fft(a,mid,inv),fft(a+mid,mid,inv);//分治
for(int i = 0;i < mid;i++)
{
cp x(cos(2*pi*i/n),inv*sin(2*pi*i/n));//inv取决是否取共轭复数
b[i]=a[i]+x*a[i+mid],b[i+mid]=a[i]-x*a[i+mid];
}
for(int i = 0;i < a;i++)a[i]=b[i];
}
每个位置分治后最终的位置是二进制翻转后的位置
void fft(cp *a,int n,int inv)
{
int bit=0;
while ((1<<bit)<n)bit++;
fo(i,0,n-1)
{
rev[i]=(rev[i>>1]>>1)|((i&1)<<(bit-1));
if (i<rev[i])swap(a[i],a[rev[i]]);//不加这条if会交换两次(就是没交换)
}
for (int mid=1;mid<n;mid*=2)//mid是准备合并序列的长度的二分之一
{
cp temp(cos(pi/mid),inv*sin(pi/mid));//单位根,pi的系数2已经约掉了
for (int i=0;i<n;i+=mid*2)//mid*2是准备合并序列的长度,i是合并到了哪一位
{
cp omega(1,0);
for (int j=0;j<mid;j++,omega*=temp)//只扫左半部分,得到右半部分的答案
{
cp x=a[i+j],y=omega*a[i+j+mid];
a[i+j]=x+y,a[i+j+mid]=x-y;//这个就是蝴蝶变换什么的
}
}
}
}
注意 lim
#include<bits/stdc++.h>
using namespace std;
const double pi = acos(-1.0);
const int N = 3e6 + 10;
struct cp {
double x, y;
cp() {}
cp(double _x, double _y) {
x = _x; y = _y;
}
cp operator + (cp b) {
return cp(x + b.x, y + b.y);
}
cp operator -(cp b) {
return cp(x - b.x, y - b.y);
}
cp operator *(cp b) {
return cp(x * b.x - y * b.y, x * b.y + y * b.x);
}
};
int rev[N];
int bit = 0;
int lim;
void FFT(cp* a, int inv) {
for (int i = 0; i < lim; i++) {
if (i < rev[i]) {
swap(a[i], a[rev[i]]);
}
}
for (int mid = 1; mid < lim; mid <<= 1) {
cp temp(cos(pi / mid), inv * sin(pi / mid));
for (int i = 0; i < lim; i += mid * 2) {
cp omega(1, 0);
for (int j = 0; j < mid; j++, omega = omega * temp) {
cp x = a[i + j], y = omega * a[i + j + mid];
a[i + j] = x + y, a[i + j + mid] = x - y;
}
}
}
}
int n, m;
cp A[N], B[N];
int main() {
scanf("%d%d", &n, &m);
lim = 1;
while (lim <= n + m)lim<<=1,bit++;//调整至 2^k
for (int i = 0; i < lim; i++) {
rev[i] = (rev[i >> 1] >> 1) | ((i & 1) << (bit - 1));
}
for (int i = 0; i <= n; i++)scanf("%lf", &A[i].x), A[i].y = 0;
for (int i = 0; i <= m; i++)scanf("%lf", &B[i].x), B[i].y = 0;
FFT(A, 1);
FFT(B, 1);
for (int i = 0; i <= lim; i++) {
A[i] = A[i] * B[i];
}
FFT(A, -1);
for (int i = 0; i <= n + m; i++) {
printf("%d ", int(A[i].x /lim+0.5));
}
}
NTT
原根
还没有整太明白
待补,丢一个板子
#include<bits/stdc++.h>
#define swap(a,b) (a^=b,b^=a,a^=b)
using namespace std;
#define LL long long
const int MAXN = 3 * 1e6 + 10, P = 998244353, G = 3, Gi = 332748118;
char buf[1 << 21], * p1 = buf, * p2 = buf;
int N, M, limit = 1, L, r[MAXN];
LL a[MAXN], b[MAXN];
inline LL fastpow(LL a, LL k) {
LL base = 1;
while (k) {
if (k & 1) base = (base * a) % P;
a = (a * a) % P;
k >>= 1;
}
return base % P;
}
inline void NTT(LL* A, int type) {
for (int i = 0; i < limit; i++)
if (i < r[i]) swap(A[i], A[r[i]]);
for (int mid = 1; mid < limit; mid <<= 1) {
LL Wn = fastpow(type == 1 ? G : Gi, (P - 1) / (mid << 1));
for (int j = 0; j < limit; j += (mid << 1)) {
LL w = 1;
for (int k = 0; k < mid; k++, w = (w * Wn) % P) {
int x = A[j + k], y = w * A[j + k + mid] % P;
A[j + k] = (x + y) % P,
A[j + k + mid] = (x - y + P) % P;
}
}
}
}
int main() {
scanf("%d%d", &N, &M);
for (int i = 0; i <= N; i++) scanf("%d", a + i);
for (int i = 0; i <= M; i++) scanf("%d", b + i);
while (limit <= N + M) limit <<= 1, L++;
for (int i = 0; i < limit; i++) r[i] = (r[i >> 1] >> 1) | ((i & 1) << (L - 1));
NTT(a, 1); NTT(b, 1);
for (int i = 0; i < limit; i++) a[i] = (a[i] * b[i]) % P;
NTT(a, -1);
LL inv = fastpow(limit, P - 2);
for (int i = 0; i <= N + M; i++)
printf("%d ", (a[i] * inv) % P);
return 0;
}
2.数据结构
线段树
#include<bits/stdc++.h>
#define int long long
using namespace std;
const int maxn = 1e6 + 10;
struct Node {
int l, r, sum, lz;
}T[maxn << 2];
void push_up(int o) {
T[o].sum = T[o << 1].sum + T[o << 1 | 1].sum;
}
void build(int o, int L, int R) {
T[o] = Node{ L,R,0,0 };
if (L == R) {
cin >> T[o].sum;
return;
}
int mid = L + R >> 1;
build(o << 1, L, mid);
build(o << 1 | 1, mid + 1, R);
push_up(o);
}
void push_down(int o) {
if (!T[o].lz)return;
T[o << 1].lz += T[o].lz;
T[o << 1 | 1].lz += T[o].lz;
T[o << 1].sum += (T[o << 1].r - T[o << 1].l + 1) * T[o].lz;
T[o << 1 | 1].sum += (T[o << 1 | 1].r - T[o << 1 | 1].l + 1) * T[o].lz;
T[o].lz = 0;
}
void updt(int o, int l, int r, int z) {
if (l <= T[o].l && T[o].r <= r) {
T[o].lz += z;
T[o].sum += z * (T[o].r - T[o].l + 1);
return;
}
push_down(o);
int mid = T[o].l + T[o].r >> 1;
if (l <= mid) updt(o << 1, l, r, z);
if (r > mid) updt(o << 1 | 1, l, r, z);
push_up(o);
}
int query(int o, int l, int r) {
if (l <= T[o].l && T[o].r <= r) {
return T[o].sum;
}
push_down(o);
int sum = 0, mid = T[o].l + T[o].r >> 1;
if (l <= mid)sum += query(o << 1, l, r);
if (r > mid)sum += query(o << 1 | 1, l, r);
return sum;
}
/*
* @Author: zhl
* @Date: 2020-10-13 20:09:58
*/
#include<bits/stdc++.h>
#define int long long
#define lo (o<<1)
#define ro (o<<1|1)
#define mid (l+r>>1)
using namespace std;
const int N = 2e6 + 10;
int sum[N], lz[N];
void build(int o, int l, int r) {
if (l == r) {
scanf("%lld", &sum[o]);
return;
}
build(lo, l, mid);
build(ro, mid + 1, r);
sum[o] = sum[lo] + sum[ro];
}
int x, y, z;
void push_down(int o, int l, int r) {
if (!lz[o])return;
lz[lo] += lz[o]; lz[ro] += lz[o];
sum[lo] += (mid - l + 1) * lz[o];
sum[ro] += (r - mid) * lz[o];
lz[o] = 0;
}
void updt(int o, int l, int r) {
if (x <= l and r <= y) {
lz[o] += z;
sum[o] += z * (r - l + 1);
return;
}
push_down(o, l, r);
if (x <= mid) updt(lo, l, mid);
if (y > mid) updt(ro, mid + 1, r);
sum[o] = sum[lo] + sum[ro];
}
int query(int o, int l, int r) {
if (x <= l and r <= y) {
return sum[o];
}
push_down(o, l, r);
int ans = 0;
if (x <= mid) ans += query(lo, l, mid);
if (y > mid) ans += query(ro, mid + 1, r);
return ans;
}
int n, m;
signed main() {
scanf("%lld%lld", &n, &m);
build(1, 1, n);
for (int i = 1; i <= m; i++) {
int op; scanf("%lld", &op);
if (op == 1) {
scanf("%lld%lld%lld", &x, &y, &z);
updt(1, 1, n);
}
else {
scanf("%lld%lld", &x, &y);
printf("%lld\n", query(1, 1, n));
}
}
}
树状数组
int lowbit(int i){
return i&(-i);
}
void insert(int ind,int val){
while(ind <= n){
C[ind] += val;
ind += lowbit(ind);
}
}
int query(int n){ //sum of A[1] + A[2] + ... + A[n]
int sum = 0;
while(n){
sum += C[n];
n -= lowbit(n);
}
return sum;
}
LCA倍增
#include<bits/stdc++.h>
#define repE(i,u) for(int i = head[u];i;i = E[i].next)
using namespace std;
const int N = 1e6 + 10;
int f[N][32];
int dep[N];
struct Edge {
int to, next;
}E[N << 1];
int head[N], tot;
void addEdge(int from, int to) {
E[++tot] = Edge{ to,head[from] };
head[from] = tot++;
}
void init(int u, int p) {
dep[u] = dep[p] + 1;
f[u][0] = p;
for (int x = 1; (1 << x) < dep[u]; x++) {
f[u][x] = f[f[u][x - 1]][x - 1];
}
repE(i, u) {
if (E[i].to == p)continue;
init(E[i].to, u);
}
}
int LCA(int x, int y) {
if (dep[x] < dep[y])swap(x, y);
while (dep[x] != dep[y]) {
int u = dep[x] - dep[y];
int v = 0;
while (!(u & (1 << v)))v++;
x = f[x][v];
}
while (x != y) {
int v = 0;
while (f[x][v] != f[y][v])v++;
x = f[x][max(0,v - 1)]; y = f[y][max(0,v - 1)];
}
return x;
}
int n, m, root;
int main() {
scanf("%d%d%d", &n, &m, &root);
for (int i = 1; i < n; i++) {
int x, y; scanf("%d%d", &x, &y);
addEdge(x, y);
addEdge(y, x);
}
init(root, 0);
for (int i = 1; i <= m; i++) {
int x, y; scanf("%d%d", &x, &y);
printf("%d\n", LCA(x, y));
}
}
ST表
#include<bits/stdc++.h>
using namespace std;
const int N = 1e5 + 10;
int f[N][32];
int A[N];
int n, m, x, y;
void init() {
for (int i = 1; i <= n; i++) {
f[i][0] = A[i];
}
for (int j = 1; (1 << j) <= n; j++) {
for (int i = 1; i + (1 << j) - 1 <= n; i++) {
f[i][j] = max(f[i][j - 1], f[i + (1 << (j-1))][j - 1]);
}
}
}
int query(int l, int r) {
int k = log2(r - l + 1);
return max(f[l][k], f[r - (1 << k) + 1][k]);
}
int main() {
scanf("%d%d", &n, &m);
for (int i = 1; i <= n; i++) {
scanf("%d", A + i);
}
init();
for (int i = 1; i <= m; i++) {
scanf("%d%d", &x, &y);
printf("%d\n", query(x, y));
}
}
主席树
/*
* @Author: zhl
* @Date: 2020-10-12 19:34:06
*/
#include<bits/stdc++.h>
#define rep(i,a,b) for(int i = a;i <= b;i++)
#define mid (l+r>>1)
using namespace std;
const int N = 5e5 + 10;
int L[N << 5], R[N << 5], sum[N << 5];
int tot;
int build(int l, int r) {
int u = ++tot;
sum[u] = 0;
if (l < r) {
L[u] = build(l, mid);
R[u] = build(mid + 1, r);
}
return u;
}
int insert(int pre, int l, int r, int pos) {
int u = ++tot;
L[u] = L[pre];
R[u] = R[pre];
sum[u] = sum[pre] + 1;
if (l >= r)return u;
if (pos <= mid) {
L[u] = insert(L[pre], l, mid, pos);
}
else {
R[u] = insert(R[pre], mid + 1, r, pos);
}
return u;
}
int query(int rt_x, int rt_y, int l, int r, int k) {
if (l == r) {
return r;
}
int num = sum[L[rt_y]] - sum[L[rt_x]];//区间内,左半区间的数的数量
if (num >= k) {
//在左边
return query(L[rt_x], L[rt_y], l, mid, k);
}
else {
return query(R[rt_x], R[rt_y], mid + 1, r, k - num);
}
}
int n, m;
int A[N], id[N], root[N];
int main() {
cin >> n >> m;
rep(i, 1, n) {
cin >> A[i];
id[i] = A[i];
}
sort(id + 1, id + 1 + n);
int cntID = unique(id + 1, id + 1 + n) - id - 1;
root[0] = build(1, cntID);
rep(i, 1, n) {
int pos = lower_bound(id + 1, id + 1 + cntID, A[i]) - id;
root[i] = insert(root[i - 1], 1, cntID, pos);
}
rep(i, 1, m) {
int x, y, k;
cin >> x >> y >> k;
int pos = query(root[x - 1], root[y], 1, cntID, k);
cout << id[pos] << endl;
}
}
可持久化01Tire
/*
* @Author: zhl
* @Date: 2020-10-13 09:46:47
*/
#include<bits/stdc++.h>
using namespace std;
#define rep(i,a,b) for(int i = a;i <= b;i++)
#define repE(i,u) for(int i = head[u];i;i = E[i].next)
#define swap(a,b) a^=b,b^=a,a^=b
const int N = 2e6 + 10;
//cnm 1e6 一直T
//N 5e5,m 5e5
const int maxbit = 30;
struct {
int root[N], c[N][2], tot;
void init() {
c[0][0] = c[0][1] = 0;
tot = 0;
root[0] = 0;
}
int getnode() {
tot++;
c[tot][0] = c[tot][1] = 0;
return tot;
}
//root[v] = insret(Tire.root[v-1], v, val);
int insert(int pre, int v, int val) {
int u = getnode();
int ans = u;
for (int i = maxbit; i >= 0; i--) {
c[u][0] = c[pre][0];
c[u][1] = c[pre][1];
int x = val & (1 << i) ? 1 : 0;
c[u][x] = getnode();
u = c[u][x];
pre = c[pre][x];
}
return ans;
}
int query(int l, int r, int x) {
int MinID = root[l];
int u = root[r];
int ans = 0;
for (int i = maxbit; i >= 0; i--) {
int now = (x & (1 << i)) ? 0 : 1;
if (c[u][now] and c[u][now] >= MinID) {
u = c[u][now];
ans += (1 << i);
}
else {
u = c[u][now ^ 1];
}
}
return ans;
}
}Tire;
int n, m, l, r, x;
int A[N];
int main() {
scanf("%d%d", &n, &m);
Tire.init();
for (int i = 1; i <= n; i++) {
scanf("%d", A + i);
Tire.root[i] = Tire.insert(Tire.root[i-1],i,A[i]);
}
for (int i = 1; i <= m; i++) {
scanf("%d%d%d", &x, &l, &r);
printf("%d\n", Tire.query(l + 1, r + 1, x));
}
}
/*
3 1
1 2 3
*/
树链剖分
/*
* @Author: zhl
* @Date: 2020-10-13 20:36:59
*/
#include<bits/stdc++.h>
#define rep(i,a,b) for(int i = a;i <= b;i++)
#define repE(i,u) for(int i = head[u];i;i = E[i].next)
#define mid (l+r>>1)
#define lo (o<<1)
#define ro (o<<1|1)
using namespace std;
const int N = 4e5 + 10;
int A[N];
int n, m, root, mod;
struct Edge {
int to, next;
}E[N << 1];
int head[N], tot;
void addEdge(int from, int to) {
E[++tot] = Edge{ to,head[from] };
head[from] = tot++;
}
int fa[N], sz[N], Tfa[N], dep[N], son[N];
//dfs1处理dep,sz,fa,son(重儿子)
void dfs1(int u, int p) {
fa[u] = p;
dep[u] = dep[p] + 1;
sz[u] = 1;
int mx = -1;
repE(i, u) {
int v = E[i].to;
if (v == p)continue;
dfs1(v, u);
sz[u] += sz[v];
if (sz[v] > mx)mx = sz[v], son[u] = v;
}
}
int cnt;
int id[N], val[N], top[N];
//dfs2 剖分数链
void dfs2(int u, int topf) {
id[u] = ++cnt;
val[cnt] = A[u];
top[u] = topf;
if (!son[u])return;
dfs2(son[u], topf);
repE(i, u) {
int v = E[i].to;
if (v == fa[u] or v == son[u])continue;
dfs2(v, v);
}
}
int sum[N << 2], lz[N << 2];
int x, y, z;
void push_down(int o, int l, int r) {
if (!lz[o])return;
lz[lo] += lz[o];
lz[ro] += lz[o];
sum[lo] = (sum[lo] + lz[o] * (mid - l + 1)) % mod;
sum[ro] = (sum[ro] + lz[o] * (r - mid)) % mod;
lz[o] = 0;
}
void build(int o, int l, int r) {
if (l == r) {
sum[o] = val[l];
return;
}
build(lo, l, mid);
build(ro, mid + 1, r);
sum[o] += sum[lo] + sum[ro];
}
void updt(int o, int l, int r) {
if (x <= l and r <= y) {
lz[o] = (lz[o] + z) % mod;
sum[o] = (sum[o] + z * (r - l + 1)) % mod;
return;
}
push_down(o, l, r);
if (x <= mid)updt(lo, l, mid);
if (y > mid)updt(ro, mid + 1, r);
sum[o] = (sum[lo] + sum[ro]) % mod;
}
int query(int o, int l, int r) {
if (x <= l and r <= y) {
return sum[o];
}
int ans = 0;
push_down(o, l, r);
if (x <= mid)ans = (ans + query(lo, l, mid)) % mod;
if (y > mid)ans = (ans + query(ro, mid + 1, r)) % mod;
return ans;
}
int query_path(int a, int b) {
int ans = 0;
while (top[a] != top[b]) {
if (dep[top[a]] < dep[top[b]])swap(a, b);
x = id[top[a]]; y = id[a];
ans = (ans + query(1, 1, cnt)) % mod;
a = fa[top[a]];
}
if (dep[a] > dep[b])swap(a, b);
x = id[a]; y = id[b];
ans = (ans + query(1, 1, cnt)) % mod;
return ans;
}
void updt_path(int a, int b, int k) {
k %= mod;
while (top[a] != top[b]) {
if (dep[top[a]] < dep[top[b]])swap(a, b);
x = id[top[a]]; y = id[a]; z = k;
updt(1, 1, cnt);
a = fa[top[a]];
}
if (dep[a] > dep[b])swap(a, b);
x = id[a]; y = id[b]; z = k;
updt(1, 1, cnt);
}
int main() {
scanf("%d%d%d%d", &n, &m, &root, &mod);
for (int i = 1; i <= n; i++)scanf("%d", A + i);
for (int i = 1; i < n; i++) {
scanf("%d%d", &x, &y);
addEdge(x, y); addEdge(y, x);
}
dfs1(root, 0);
dfs2(root, root);
build(1, 1, cnt);
while (m--) {
int op; scanf("%d", &op);
int a, b, c;
if (op == 1) {//a,b 路径 + c
scanf("%d%d%d", &a, &b, &c);
updt_path(a, b, c);
}
if (op == 2) {//a,b 路径sum
scanf("%d%d", &a, &b);
printf("%d\n", query_path(a, b));
}
if (op == 3) {//a的subtree + c
scanf("%d%d", &a, &c);
x = id[a]; y = id[a] + sz[a] - 1;
z = c;
updt(1, 1, cnt);
}
if (op == 4) {
scanf("%d", &a);
x = id[a]; y = id[a] + sz[a] - 1;
printf("%d\n", query(1, 1, cnt));
}
}
}
/*
5 50 2 24000
7 3 7 8 0
1 2
1 5
3 1
4 1
*/
单调队列(滑动窗口)
class Solution {
public:
vector<int> maxSlidingWindow(vector<int>& nums, int k) {
deque<int>dq;
vector<int>ans;
int n = nums.size();
for(int i = 0;i < n;i++){
if(!dq.empty() && dq.front() == i - k)dq.pop_front();
while(!dq.empty() && nums[dq.back()] < nums[i]) dq.pop_back();
dq.push_back(i);
if(i >= k - 1) ans.push_back(nums[dq.front()]);
}
return ans;
}
};
\(deque\) 里存的是数组的下标
单调栈
3.图论
最短路算法
Dijkstra
复杂度: \(O(VlgV+E)\)
struct Node {
long long d;
int u;
bool operator < (const Node& rhs)const {
//rhs,right-hand-side
return d > rhs.d;
}
};
void dijkstra(int s) {
priority_queue<Node>Q;
for (int i = 1;i <= n;i++) {
d[i] = INF;
}
d[s] = 0;
memset(vis, 0, sizeof(vis));
Q.push(Node{ 0,s });
while (!Q.empty()) {
Node x = Q.top();Q.pop();
int u = x.u;
if (vis[u]) {
continue;
}
vis[u] = 1;
for (int i = 0;i < G[u].size();i++) {
Edge e = edges[G[u][i]];
if (d[e.to] > d[u] + e.dist) {
d[e.to] = d[u] + e.dist;
p[e.to] = G[u][i];
Q.push(Node{ d[e.to],e.to });
}
}
}
}
bellman-ford
复杂度 : \(O(EV)\)
struct E {
int from, to, w;
}Edges[maxm];
int dis[maxn];
int n, m, s;
bool bellman_ford(int s) {
for (int i = 1; i < n; i++) {
for (int j = 0; j < m; j++) {
int f = Edges[j].from, t = Edges[j].to, w = Edges[j].w;
if (dis[t] > dis[f] + w) {
dis[t] = dis[f] + w;
}
}
}
for (int j = 0; j < m; j++) {
int f = Edges[j].from, t = Edges[j].to, w = Edges[j].w;
if (dis[t] > dis[f] + w) {
return false;
}
}
return true;
}
SPFA
\(O(kE)\) ,\(k\) 为每个节点入队次数
最坏 \(O(EV)\)
void spfa() {
queue<int> q;
memset(dis, 0x3f, sizeof(int) * (n + 10));
memset(vis, 0, sizeof(int) * (n + 10));
q.push(s); dis[s] = 0; vis[s] = 1; //第一个顶点入队,进行标记
while (!q.empty()) {
int u = q.front();
q.pop(); vis[u] = 0;
for (int i = head[u]; ~i; i = E[i].next) {
int v = E[i].to;
if (dis[v] > dis[u] + E[i].w) {
dis[v] = dis[u] + E[i].w;
if (vis[v] == 0) {
//此处判环
//if(++cnt[v] >= n) 有负环
vis[v] = 1;
q.push(v);
}
}
}
}
}
二分图匹配 (匈牙利算法)
#include<iostream>
#include<cstring>
using namespace std;
const int maxn = 505;
int match[maxn];
int G[maxn][maxn];
int vis[maxn];
int n, k;
bool dfs(int u) {
for (int i = 1;i <= n;i++) {
if (G[u][i] && !vis[i]) {
vis[i] = 1;
if (match[i] == 0 || dfs(match[i])) {
match[i] = u;
return true;
}
}
}
return false;
}
int main() {
cin >> n >> k;
int a, b;
for (int i = 0;i < k;i++) {
cin >> a >> b;
G[a][b] = 1;
}
int cnt = 0;
for (int i = 1;i <= n;i++) {
memset(vis, 0, sizeof(vis));
if (dfs(i))
cnt++;
}
cout << cnt;
}
网络流
Dinic
#include<bits/stdc++.h>
#define int long long
using namespace std;
const int maxn = 5e5 + 10;
const int inf = 0x3f3f3f3f3f3f3f3f;
int n, m, s, t, tot, head[maxn];
int ans, dis[maxn], now[maxn];
struct Edge {
int to, next, val;
}E[maxn];
void AddEdge(int from, int to, int w) {
E[tot] = Edge{ to,head[from],w };
head[from] = tot++;
E[tot] = Edge{ from,head[to],0 };
head[to] = tot++;
}
int bfs() {
for (int i = 1; i <= n; i++) dis[i] = inf;
queue<int>Q;
Q.push(s);
dis[s] = 0;
now[s] = head[s];
while (!Q.empty()) {
int u = Q.front();
Q.pop();
for (int i = head[u]; ~i; i = E[i].next) {
int v = E[i].to;
if (E[i].val > 0 && dis[v] == inf) {
Q.push(v);
dis[v] = dis[u] + 1;
now[v] = head[v];
if (v == t)return 1; //分层成功
}
}
}
return 0;
}
int dfs(int x, int sum) {
if (x == t)return sum;
int k, res = 0;
for (int i = now[x]; ~i && sum; i = E[i].next) {
now[x] = i;
int v = E[i].to;
if (E[i].val > 0 && (dis[v] == dis[x] + 1)) {
k = dfs(v, min(sum, E[i].val));
if (k == 0) dis[v] = inf;
E[i].val -= k;
E[i ^ 1].val += k;
res += k;
sum -= k;
}
}
return res;
}
signed main() {
ios::sync_with_stdio(0); cin.tie(0); cout.tie(0);
cin >> n >> m >> s >> t;
memset(head, -1, sizeof(int) * (n + 10));
for (int i = 1; i <= m; i++) {
int f, t, w;
cin >> f >> t >> w;
AddEdge(f, t, w);
}
while (bfs()) {
ans += dfs(s, inf);
}
cout << ans << endl;
}
最小费用最大流
MCMF
#include<bits/stdc++.h>
using namespace std;
const int maxn = 5e3 + 10;
const int maxm = 5e4 + 10;
struct E {
int to, dis, flow, next;
}e[maxm<<1];
int tot, head[maxn];
void addEdge(int from, int to, int flow, int dis) {
e[tot] = E{ to, dis, flow,head[from] };
head[from] = tot++;
}
int vis[maxn], dis[maxn], incf[maxn], pre[maxn];
int s, t, n, m;
int maxflow, mincost;
bool spfa() {
queue<int>Q;
memset(dis, 0x3f, sizeof dis);
memset(vis, 0, sizeof vis);
Q.push(s);
dis[s] = 0;
vis[s] = 1;
incf[s] = 1 << 30;
while (!Q.empty()) {
int u = Q.front(); Q.pop();
vis[u] = 0;
for (int i = head[u]; i != -1; i = e[i].next) {
if (!e[i].flow) continue; //没有残余流量
int v = e[i].to;
if (dis[v] > dis[u] + e[i].dis) {
dis[v] = dis[u] + e[i].dis;
incf[v] = min(incf[u], e[i].flow);
pre[v] = i;
if (!vis[v]) vis[v] = 1, Q.push(v);
}
}
}
if (dis[t] == 0x3f3f3f3f)return 0;
return 1;
}
void MCMF() {
while (spfa()) {
int x = t;
maxflow += incf[t];
mincost += dis[t] * incf[t];
while (x != s) {
int p = pre[x];
e[p].flow -= incf[t];
e[p ^ 1].flow += incf[t];
x = e[p ^ 1].to;
}
}
}
signed main() {
scanf("%d%d%d%d", &n, &m, &s, &t);
memset(head, -1, sizeof head);
for (int f, t, w, x, i = 1; i <= m; i++) {
scanf("%d%d%d%d", &f, &t, &w, &x);
addEdge(f, t, w, x);
addEdge(t, f, 0, -x);
}
MCMF();
printf("%d %d\n", maxflow, mincost);
}
一般图匹配(开花算法)
#include<bits/stdc++.h>
using namespace std;
const int maxn = 600;
const int maxm = 400000; //双向边要开两倍!!
struct Edge {
int to, nxt;
}E[maxm];
int head[maxn];
int tot;
int gcd(int x,int y)
{
return y?gcd(y,x%y):x;
}
void addEdge(int from, int to, bool istwo = false) {
E[tot] = Edge{ to,head[from] };
head[from] = tot++;
//双向边
if (istwo) {
E[tot] = Edge{ from,head[to] };
head[to] = tot++;
}
}
int fa[maxn];//并查集
int find(int x) {
return fa[x] == x ? x : fa[x] = find(fa[x]);
}
void merge(int a, int b) {
a = find(a);
b = find(b);
if (a != b) fa[a] = b;
}
int n, m;
int match[maxn];//记录匹配
int Q[maxn], rear;//队列
int nxt[maxn], mark[maxn], vis[maxn];
int LCA(int x, int y) {
static int t = 0; t++;
while (1) {
if (x != -1) {
x = find(x); //点对应到花
if (vis[x] == t)return x;
vis[x] = t;
if (match[x] != -1) x = nxt[match[x]]; // 有匹配,向上走
else x = -1;//停下来
}
swap(x, y);
}
}
void group(int a, int p) {
while (a != p) {
int b = match[a], c = nxt[b];
if (find(c) != p) nxt[c] = b;
if (mark[b] == 2) mark[Q[rear++] = b] = 1;
if (mark[c] == 2) mark[Q[rear++] = c] = 1;
merge(a, b); merge(b, c);
a = c;
}
}
void aug(int s) {
for (int i = 1; i <= n; i++) {
nxt[i] = -1; fa[i] = i; mark[i] = 0; vis[i] = -1;
}
mark[s] = 1;
Q[0] = s; rear = 1;
for (int front = 0; match[s] == -1 && front < rear; front++) {
int x = Q[front];
for (int i = head[x]; i != -1; i = E[i].nxt) {
int y = E[i].to;
if (match[x] == y) continue; // x与y已匹配,忽略
if (find(x) == find(y)) continue; // x与y同在一朵花,忽略
if (mark[y] == 2) continue; // 偶环,忽略
if (mark[y] == 1) { // 缩点
int r = LCA(x, y); //
if (find(x) != r) nxt[x] = y;
if (find(y) != r) nxt[y] = x;
group(x, r);
group(y, r);
}
else if (match[y] == -1) { // y自由,可以增广
nxt[y] = x;
for (int u = y; u != -1; ) { // 交叉链取反
int v = nxt[u];
int mv = match[v];
match[v] = u, match[u] = v;
u = mv;
}
break; // 搜索成功,退出循环将进入下一阶段
}
else {
nxt[y] = x;
mark[Q[rear++] = match[y]] = 1;
mark[y] = 2;
}
}
}
}
void init() {
memset(head, -1, sizeof head);
}
int main() {
scanf("%d",&n);
init();
for(int i = 1;i <= n;i++){
for(int j = 1;j <= i;j++){
if(gcd(i,j) != 1){
addEdge(i,j,true);
}
}
}
for (int i = 1; i <= n; i++) {
match[i] = -1;
}
for (int i = 1; i <= n; i++) {
if (match[i] == -1) aug(i);
}
int tot = 0;
for (int i = 1; i <= n; i++) {
if (match[i] != -1)tot++;
else match[i] = 0;
}
printf("%d\n", tot>>1);
memset(vis,0,sizeof vis);
for (int i = 1; i <= n; i++) {
if(vis[i])continue;
printf("%d %d%s", i, match[i], i == n ? "\n" : " ");
vis[i] = 1;vis[match[i]] = 1;
}
}
树的重心
void getroot(int u, int f) { //f 是 father 节点
// 找质心
sz[u] = 1;
int mxchild = 0; //子树的最大节点数
for(int i = head[u]; i != -1; i = e[i].nxt ) {
int v = e[i].to;
if(v != f && !visit[v]) {
getroot(v, u);
sz[u] += sz[v];
mxchild = max(mxchild, sz[v]);
}
}
int tmp = max(mxchild, subtreesize - sz[u]); //父子树的最大节点个数
if(tmp < nowmn) {
nowmn = tmp;
rt = u;
}
}
树的直径
int dfs(int u,int pre){
int maxpath = 0;
for(int v : adj[u]){
if(v == pre)continue;
dep[v] = dep[u] + 1;
int nowpath = 1 + dfs(v,u);
d = max(d,nowpath + maxpath); //d 是树的直径
maxpath = max(maxpath,nowpath);
}
return maxpath;
}
4.字符串
KMP
int kmp(){
int i = 0,j = 0;
while(i < n){ // m 是匹配串的长度
if(j == - 1 || s[i] == p[j]){
j++;
i++;
if(j == m){
return i - j + 1;
}
}
else{
j = next[j];
}
}
return -1; //没有找到匹配的子串
}
void get_next(){
next[0] = -1;
int k = -1;
int j = 0;
while(j < m){
if(k == -1 || p[k] == p[j]){
k++;j++;
next[j] = k;
}
else{
k = next[k];
}
}
}
Manacher
/*
* @Author: zhl
* @Date: 2020-10-14 11:36:53
*/
class Solution {
public:
string longestPalindrome(string s) {
int n = s.length();
if(n < 2)return s;
string t = "$";
for(int i = 0;i < n;i++){
t += "#"+s.substr(i,1);
}
t += "#@";
n = t.length();
vector<int>p;p.resize(n+10);
int id = 0,mx = 0;int maxlen = 0,cen = 0;
for(int i = 1;i < n - 1;i++){
p[i] = i < mx ? min(mx-i,p[2*id-i]) : 1;
while(t[i-p[i]] == t[i+p[i]])p[i]++;
if(i+p[i]>mx){
mx = i+p[i];
id = i;
}
if(p[i] - 1 > maxlen){
maxlen = p[i]-1;
cen = i;
}
}
int st = (cen - maxlen)/2;
return s.substr(st,maxlen);
}
};
AC自动机
#include<bits/stdc++.h>
using namespace std;
const int N = 6e6 + 10;
queue<int>q;
struct {
int c[N][26], fail[N], val[N], cnt;
void insert(char* s) {
int len = strlen(s); int now = 0;
for (int i = 0; i < len; i++) {
int v = s[i] - 'a';
if (!c[now][v])c[now][v] = ++cnt;
now = c[now][v];
}
val[now]++;
}
void getFail() {
for (int i = 0; i < 26; i++) {
if (c[0][i])fail[c[0][i]] = 0, q.push(c[0][i]);
}
while (!q.empty()) {
int u = q.front(); q.pop();
for (int i = 0; i < 26; i++) {
if (c[u][i]) {
fail[c[u][i]] = c[fail[u]][i];
q.push(c[u][i]);
}
else c[u][i] = c[fail[u]][i];
}
}
}
int query(char* s) {
int len = strlen(s); int now = 0, ans = 0;
for (int i = 0; i < len; i++) {
now = c[now][s[i] - 'a'];
for (int t = now; t && val[t] != -1; t = fail[t]) {
ans += val[t];
val[t] = -1;
}
}
return ans;
}
}Ac;
int n;
char p[N];
int main() {
scanf("%d", &n);
for (int i = 1; i <= n; i++) {
scanf("%s", p);
Ac.insert(p);
}
Ac.getFail();
scanf("%s", p);
printf("%d\n", Ac.query(p));
}
最小表示法
5.计算几何
const double eps = 1e-8;
const double inf = 1e20;
const double pi = acos(-1.0);
int sgn(double x) {
if (fabs(x) < eps)return 0;
if (x < 0) return -1;
else return 1;
}
struct Point {
double x, y;
Point() {}
Point(double _x, double _y) {
x = _x;
y = _y;
}
void input() { scanf("%lf%lf", &x, &y); }
void output() { printf("%.2f %.2f\n", x, y); }
bool operator == (Point b)const {
return sgn(x - b.x) == 0 and sgn(y - b.y) == 0;
}
bool operator <(const Point& b)const {
//左下小
return sgn(x - b.x) == 0 ? sgn(y - b.y) < 0 : x < b.x;
}
Point operator -(const Point& b)const {
return Point{ x - b.x,y - b.y };
}
double operator ^(const Point& b)const {
//叉积
return x * b.y - y*b.x;
}
double operator *(const Point& b)const {
return x * b.x + y * b.y;
}
double len() {
return sqrt(x * x + y * y);
//return hypot(x, y);
}
double len2() {
return x * x + y * y;
}
double distance(Point p) {
return sqrt((x-p.x)*(x-p.x)+(y-p.y)*(y-p.y));
//return hypot(x - p.x, y - p.y);
}
Point operator +(const Point& b)const {
return Point{ x + b.x, y + b.y };
}
Point operator *(const double& k)const {
return Point{ x * k, y * k };
}
Point operator /(const double& k)const {
return Point{ x / k, y / k };
}
double rad(Point a, Point b) {
Point p = *this;
return fabs(atan2(fabs((a - p) ^ (b - p)),((a - p) * (b - p))));
}
Point rotleft() {
return Point{ -y,x };
}
//顺时针旋转 90 度
Point rotright() {
return Point{ y, -x };
}
//绕着 p 点逆时针旋转 angle
Point rotate(Point p, double angle) {
Point v = (*this) - p;
double c = cos(angle), s = sin(angle);
return Point{ p.x + v.x * c - v.y * s, p.y + v.x * s + v.y * c };
}
};
struct Line {
Point s, e;
bool operator == (Line v) {
return s == v.s and e == v.e;
}
Line() {}
Line(Point _s, Point _e) {
s = _s;
e = _e;
}
Line(Point p, double angle) {
s = p;
if (sgn(angle - pi / 2) == 0) {
e = s + Point{ 0,1 };
}
else {
e = s + Point{ 1,tan(angle) };
}
}
//ax + by + c = 0;
Line(double a, double b, double c) {
if (sgn(a) == 0) {
s = Point{ 0,-c / b };
e = Point{ 1,-c / b };
}
else if (sgn(b) == 0) {
s = Point{ -c / a, 0 };
e = Point{ -c / a, 1 };
}
else {
s = Point{ 0,-c / b };
e = Point{ 1,(-c - a) / b };
}
}
void input() {
s.input();//start
e.input();//end
}
void adjust() {
if (e < s)swap(s, e);
}
double length() {
return s.distance(e);
}
double angle() {
double k = atan2(e.y - s.y, e.x - s.x);
if (sgn(k) < 0) k += pi;
if (sgn(k - pi) == 0) k -= pi;
return k;
}
//1 left
//2 right
//3 on line
int relation(Point p) {
int c = sgn((p - s) ^ (e - s));
if (c < 0)return 1;
else if (c > 0)return 2;
else return 3;
}
bool pointOnSeg(Point p) {
return sgn((p - s) ^ (e - s)) == 0 and sgn((p - s) * (e - s)) <= 0;
}
bool parallel(Line v) {
return (sgn((e - s) ^ (v.e - v.s)) == 0);
}
//seg cross seg
//2 规范相交
//1 非规范相交
//0 不相交
int segCrossSeg(Line v) {
int d1 = sgn((e - s) ^ (v.s - s));
int d2 = sgn((e - s) ^ (v.e - s));
int d3 = sgn((v.e - v.s) ^ (s - v.s));
int d4 = sgn((v.e - v.s) ^ (e - v.s));
if ((d1 ^ d2) == -2 and (d3 ^ d4) == -2)return 2;//-2 = (-1)^1
return (d1 == 0 and sgn((v.s - s) * (v.s - e)) <= 0) or
(d2 == 0 and sgn((v.e - s) * (v.e - e)) <= 0) or
(d3 == 0 and sgn((s - v.s) * (s - v.e)) <= 0) or
(d4 == 0 and sgn((e - v.s) * (e - v.e)) <= 0);
}
//Line cross seg
//2 规范相交
//1 非规范相交
//0 不相交
int lineCrossSeg(Line v) {
int d1 = sgn((e - s) ^ (v.s - s));
int d2 = sgn((e - s) ^ (v.e - s));
if ((d1 ^ d2) == -2)return 2;
return(d1 == 0 or d2 == 0);
}
//Line cross Line
//2 相交
//1 重合
//0 平行
int lineCrossLine(Line v) {
if (this->parallel(v))
return v.relation(s) == 3;
return 2;
}
//求两直线的交点
Point crossPoint(Line v) {
double a1 = (v.e - v.s) ^ (s-v.s);
double a2 = (v.e-v.s) ^ (e-v.s);
return Point{ (s.x * a2 - e.x * a1) / (a2 - a1), (s.y * a2 - e.y * a1) / (a2 - a1) };
}
//点到直线的距离
double disPointToLine(Point p) {
return fabs((p - s) ^ (e - s)) / length();
}
//点到线段的距离
double disPointToSeg(Point p) {
if (sgn((p - s) * (e - s)) < 0 or sgn((e - s) * (s - e)) < 0)
return min(p.distance(s), p.distance(e));
return disPointToLine(p);
}
//线段到线段的距离
double disSegToSeg(Line v) {
return min(min(disPointToSeg(v.s), disPointToSeg(v.e)), min(v
.disPointToSeg(s), v.disPointToSeg(e)));
}
//p 在直线上的投影
Point lineProg(Point p) {
return s + (((e - s) * ((e - s) * (p - s))) / ((e - s).len2()));
}
//p 关于直线对称的点
Point symmetryPoint(Point p) {
Point q = lineProg(p);
return Point{ 2 * q.x - p.x, 2 * q.y - p.y };
}
};
反演
反演
定义
若 \(OP\cdot OP' = r^2\) , 则称 \(P\) 与 \(P'\) 关于 \(O\) 互为反演,反演半径为 \(r\)
该题反演后,转化为很简单的问题