模板 v0.2
数学
exgcd
void exgcd(ll a, ll b, ll &x, ll &y)
{
if (!b)
{
x = 1, y = 0;
return;
}
exgcd(b, a % b, x, y);
int t = x;
x = y;
y = t - a / b * y;
}
FFT
//FFT求卷积
#include <cstdio>
#include <iostream>
#include <cstring>
#include <algorithm>
#include <vector>
#include <cmath>
#define ll long long
#define N 4000009
#define reg register
#define pi 3.141592653589793
using namespace std;
struct complex
{
double x, y;
complex(double x = 0, double y = 0) :
x(x), y(y) {}
inline complex operator+(const complex b) const
{
return complex(x + b.x, y + b.y);
}
inline complex operator-(const complex b) const
{
return complex(x - b.x, y - b.y);
}
inline complex operator*(const complex b) const
{
complex res;
res.x = x * b.x - y * b.y;
res.y = x * b.y + y * b.x;
return res;
}
};
inline void read(int &x);
void print(int x);
inline void FFT(complex *a, int type, int lim);
int n, m;
complex F[N];
int rev[N];
signed main()
{
int qwq, t, lim = 1, l = -1;
read(n), read(m);
for (reg int i = 0; i <= n; ++i) read(t), F[i].x = t;
for (reg int i = 0; i <= m; ++i) read(t), F[i].y = t; //把G(x)放到F(x)的虚部上
t = n + m;
n = max(n, m);
while (lim <= (n << 1))
{
lim <<= 1;
++l;
}
for (reg int i = 1; i <= lim; ++i)
rev[i] = (rev[i >> 1] >> 1) | ((i & 1) << l);
FFT(F, 1, lim);
for (reg int i = 0; i <= lim; ++i)
F[i] = F[i] * F[i]; //求出F(x)^2
FFT(F, -1, lim);
for (reg int i = 0; i <= t; ++i)
{
qwq = F[i].y / 2 + 0.5; //虚部取出来除2,注意要+0.5,否则精度会有问题
print(qwq);
putchar(' ');
}
return 0;
}
inline void FFT(complex *a, int type, int lim)
{ //没什么好说的,FFT的模板
for (reg int i = 1; i <= lim; ++i)
{
if (i >= rev[i]) continue;
swap(a[i], a[rev[i]]);
}
reg complex rt, w, x, y;
for (reg int mid = 1; mid < lim; mid <<= 1)
{
reg int r = mid << 1;
rt = complex(cos(pi / mid), type * sin(pi / mid));
for (reg int j = 0; j < lim; j += r)
{
w = complex(1, 0);
for (reg int k = 0; k < mid; ++k)
{
x = a[j | k];
y = w * a[j | k | mid];
a[j | k] = x + y;
a[j | k | mid] = x - y;
w = w * rt;
}
}
}
if (type == 1) return;
for (reg int i = 0; i <= lim; ++i)
{
a[i].y = a[i].y / lim;
a[i].x = a[i].x / lim;
}
}
inline void read(int &x)
{
x = 0;
char c = getchar();
while (c < '0' || c > '9') c = getchar();
while (c >= '0' && c <= '9')
{
x = (x << 3) + (x << 1) + (c ^ 48);
c = getchar();
}
}
void print(int x)
{
if (x > 9) print(x / 10);
putchar(x % 10 + '0');
}
NTT
//NTT求卷积
#include <cstdio>
#define getchar() (p1 == p2 && (p2 = (p1 = buf) + fread(buf, 1, 1 << 21, stdin), p1 == p2) ? EOF : *p1++)
#define swap(x, y) x ^= y, y ^= x, x ^= y
#define LL long long
const int MAXN = 3 * 1e6 + 10, P = 998244353, G = 3, Gi = 332748118;
char buf[1 << 21], *p1 = buf, *p2 = buf;
inline int read()
{
char c = getchar();
int x = 0, f = 1;
while (c < '0' || c > '9')
{
if (c == '-') f = -1;
c = getchar();
}
while (c >= '0' && c <= '9') x = x * 10 + c - '0', c = getchar();
return x * f;
}
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()
{
N = read();
M = read();
for (int i = 0; i <= N; i++) a[i] = (read() + P) % P;
for (int i = 0; i <= M; i++) b[i] = (read() + P) % P;
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;
}
线性求逆元
inv[1] = 1;
for (int i = 2; i <= n; ++i)
{
inv[i] = (long long)(p - p / i) * inv[p % i] % p;
}
线性求n个数逆元
s[0] = 1;
for (int i = 1; i <= n; ++i) s[i] = s[i - 1] * a[i] % p;
sv[n] = qpow(s[n], p - 2);
// 当然这里也可以用 exgcd 来求逆元,视个人喜好而定.
for (int i = n; i >= 1; --i) sv[i - 1] = sv[i] * a[i] % p;
for (int i = 1; i <= n; ++i) inv[i] = sv[i] * s[i - 1] % p;
自适应辛普森积分
double simpson(double l, double r) {
double mid = (l + r) / 2;
return (r - l) * (f(l) + 4 * f(mid) + f(r)) / 6; // 辛普森公式
}
double asr(double l, double r, double eqs, double ans, int step) {
double mid = (l + r) / 2;
double fl = simpson(l, mid), fr = simpson(mid, r);
if (abs(fl + fr - ans) <= 15 * eqs && step < 0)
return fl + fr + (fl + fr - ans) / 15; // 足够相似的话就直接返回
return asr(l, mid, eqs / 2, fl, step - 1) +
asr(mid, r, eqs / 2, fr, step - 1); // 否则分割成两段递归求解
}
double calc(double l, double r, double eps) {
return asr(l, r, eps, simpson(l, r), 12);
}
二次剩余(cipolla)
给出 N,p,求解方程 $x^2 \equiv N(\bmod ~p) $
struct cp
{
ll x, y;
cp(ll _x = 0, ll _y = 0) { x = _x, y = _y; }
cp operator*(cp b) const
{
cp res;
res.x = ((x * b.x) % mod + ((y * b.y) % mod) * fuck % mod) % mod;
res.y = (x * b.y + y * b.x) % mod;
return res;
}
};
ll fp(ll a, ll b)
{
ll c = 1;
while (b > 1)
{
if (b & 1) c *= a, c %= mod;
a *= a, b >>= 1, a %= mod;
}
return (a * c) % mod;
}
cp fp1(cp a, ll b)
{
cp c = {1, 0};
while (b > 1)
{
if (b & 1) c = c * a;
a = a * a, b >>= 1;
}
return a * c;
}
ll cipolla(ll n)
{
srand(time(0));
n %= mod;
ll a;
while (1)
{
a = rand() % mod;
fuck = (a * a - n + mod) % mod;
if (fp(fuck, (mod - 1) / 2) == mod - 1) break;
}
return fp1(cp(a, 1), (mod + 1) / 2).x;
}
常用泰勒展开
\[\large e^x=1+x+\frac{x^2}{2!}+\frac{x^3}{3!}+\dots \\
\large sin(x)=x-\frac{x^3}{3!}+\frac{x^5}{5!}-\dots \\
\large cos(x)=1-\frac{x^2}{2!}+\frac{x^4}{4!}-\dots \\
\large arcsin(x)=x+\frac{x^3}{3!} \\
\large \frac{1}{1-x}=1+x+x^2+x^3+\dots \\
\large \frac{1}{1+x}=1-x+x^2-x^3+\dots \\
\large ln(1+x)=x-\frac{x^2}{2}+\frac{x^3}{3}-\frac{x^4}{4}+\dots \\
\large \frac{1}{1+x^2}=1-x^2+x^4-x^6+\dots \\
\large tan(x)=x+\frac{x^3}{3} \\
\large arctan(x)=x-\frac{x^3}{3}+\frac{x^5}{5}-\frac{x^7}{7}+\dots
\]
BSGS
给定一个质数 \(p\),以及一个整数 %b%,一个整数 \(n\),现在要求你计算一个最小的非负整数 \(l\),满足 \(b^l \equiv n \pmod p\)
ll bsgs(ll a, ll b, ll p)
{
if (a == 0)
{
if (b == 0) return 1;
return -1;
}
a %= p, b %= p;
mp.clear();
ll t = sqrt(p), a1 = fp(a, t), x = 1;
for (int i = 0; i < t; i++)
{
mp[x] = i;
x *= a1, x %= p;
}
x = b;
for (int j = 0; j < t; j++)
{
ll val = -1;
if (mp.find(x) != mp.end()) val = mp[x];
if (val > 0 && val * t - j >= 0) return val * t - j;
x *= a, x %= p;
}
return -1;
}
特征方程求通项公式
设数列 \(\{x_n\}\)的前两项 \(x_1\) , \(x_2\) 已知,且 \(x_{n+1}=px_n+qx_{n-1}\),则称方程 \(x^2-px-q=0\)为该数列的特征方程。该方程若有两个根 \(a\) , \(b\) ,则称这两个根为该数列的特征根。
因此设数列 \(x_n=\alpha \cdot a^{n-1}+\beta \cdot b^{n-1}\)
由
\[\large
\left
\{\begin{align}
& x_1=\alpha + \beta\\
& x_2=\alpha \cdot a+\beta \cdot b\\
\end{align}
\right.
\]
解得
\[\large
\left
\{\begin{align}
& \alpha=\frac{x_2-bx_1}{a-b} \\
& \beta=\frac{x_2-ax_1}{b-a} \\
\end{align}
\right.
\]
因此数列的通项公式为 \(\large x_n=\frac{x_2-bx_1}{a-b}\cdot a^{n-1}+\frac{x_2-ax_1}{b-a}\cdot b^{n-1}\)
中国剩余定理(crt)
void exgcd(ll a, ll b, ll &x, ll &y)
{
if (!b)
{
x = 1, y = 0;
return;
}
exgcd(b, a % b, x, y);
int t = x;
x = y;
y = t - a / b * y;
}
ll inv(ll a,ll p1)
{
ll x,k;
exgcd(a,p1,x,k);
x=(x+p1)%p1;
return x;
}
ll crt(int n)
{
ll ans=0,lc=1,w,m1;
for(int i=0;i<n;i++) lc*=p[i];
for(int i=0;i<n;i++)
{
w=inv(lc/p[i],p[i]);
m1=w*r[i];
ans+=m1*(lc/p[i]);
ans%=lc;
}
return ans;
}
扩展中国剩余定理
ll c[maxn], p[maxn]; //x=c (mod p)
void exgcd(ll a, ll b, ll &x, ll &y)
{
if (!b)
{
x = 1, y = 0;
return;
}
exgcd(b, a % b, x, y);
ll t = x;
x = y;
y = t - a / b * y;
}
ll slowm(ll a, ll b, ll p1)
{
if (a == 0 || b == 0) return 0;
ll c1 = 0;
while (b > 1)
{
if (b & 1) c1 += a, c1 %= p1;
a += a, b >>= 1, a %= p1;
}
return (a + c1) % p1;
}
ll excrt(int n)
{
ll x, y;
ll p1 = p[0];
ll ans = c[0];
for (int i = 1; i < n; i++)
{
ll cc = (c[i] - ans % p[i] + p[i]) % p[i];
ll g = __gcd(p1, p[i]);
if (cc % g != 0)
return -1;
exgcd(p1, p[i], x, y);
ll p2 = p[i] / g;
x = slowm(x, cc / g, p2);
ans += x * p1;
p1 *= p2;
ans = (ans % p1 + p1) % p1;
}
return (ans % p1 + p1) % p1;
}
miller rabin判素数
int miller_rabin(ll P)
{
int Test[10] = {2, 3, 5, 7, 11, 13, 17, 19};
if (P == 1) return 0;
int t = P - 1, k = 0;
while (!(t & 1)) k++, t >>= 1;
for (int i = 0; i < 8; i++)
{
if (P == Test[i]) return 1;
ll a = fp(Test[i], t, P), nxt = a;
for (int j = 1; j <= k; j++)
{
nxt = (a * a) % P;
if (nxt == 1 && a != 1 && a != P - 1) return 0;
a = nxt;
}
if (a != 1) return 0;
}
return 1;
}
矩阵
template <int row, int col>
struct Matrix
{
int r, c;
long long ele[row][col];
Matrix() { throw "Please set row and column!"; }
Matrix(int a, int b) : r(a), c(b) { memset(ele, 0, sizeof(ele)); }
long long &operator()(int a, int b) { return ele[a][b]; }
Matrix &operator+=(Matrix oth) { return *this = *this + oth; }
Matrix &operator-=(Matrix oth) { return *this = *this - oth; }
Matrix &operator*=(Matrix oth) { return *this = *this * oth; }
};
template <int m, int n>
Matrix<m, n> operator+(Matrix<m, n> m1, Matrix<m, n> m2)
{
Matrix<m, n> ret;
for (int i = 0; i < m1.r; i++)
for (int j = 0; j < m1.c; j++)
ret(i, j) = m1(i, j) + m2(i, j);
return ret;
}
template <int m, int n>
Matrix<m, n> operator-(Matrix<m, n> m1, Matrix<m, n> m2)
{
Matrix<m, n> ret;
for (int i = 0; i < m1.r; i++)
for (int j = 0; j < m1.c; j++)
ret(i, j) = m1(i, j) + m2(i, j);
return ret;
}
template <int m, int n, int p>
Matrix<m, p> operator*(Matrix<m, n> m1, Matrix<n, p> m2) //普通乘法
{
Matrix<m, p> ret(m1.r, m2.c);
for (int i = 0; i < m1.r; i++)
for (int k = 0; k < m1.c; k++)
for (int j = 0; j < m2.c; j++)
ret(i, j) += m1(i, k) * m2(k, j);
return ret;
}
template <int m, int n>
Matrix<m, n> operator^(Matrix<m, n> mat, long long k) //快速幂
{
Matrix<m, n> ans = mat;
for (k--; k; mat *= mat, k >>= 1)
if (k & 1)
ans *= mat;
return ans;
}
template <int m, int n, int p>
Matrix<m, p> mulmod(Matrix<m, n> m1, Matrix<n, p> m2, int mod) //乘加取模
{
Matrix<m, p> ret(m1.r, m2.c);
for (int i = 0; i < m1.r; i++)
for (int k = 0; k < m1.c; k++)
for (int j = 0; j < m2.c; j++)
ret(i, j) = (ret(i, j) + m1(i, k) * m2(k, j) % mod) % mod;
return ret;
}
template <int m, int n>
Matrix<m, n> qpowmod(Matrix<m, n> mat, long long k, int mod) //快速幂取模
{
Matrix<m, n> ans = mat;
for (k--; k; mat = mulmod(mat, mat, mod), k >>= 1)
if (k & 1)
ans = mulmod(ans, mat, mod);
return ans;
}
template <int m, int n>
ostream &operator<<(ostream &os, Matrix<m, n> &mat)
{
for (int i = 0; i < mat.r; i++)
{
for (int j = 0; j < mat.c; j++)
os << mat(i, j) << ' ';
os << endl;
}
return os;
}
template <int m, int n>
istream &operator>>(istream &is, Matrix<m, n> &mat)
{
for (int i = 0; i < mat.r; i++)
for (int j = 0; j < mat.c; j++)
is >> mat(i, j);
return is;
}
const int maxn = 105;
int main()
{
int n;
long long k;
const int p = 1e9 + 7;
cin >> n >> k;
Matrix<maxn, maxn> mat(n, n);
cin >> mat;
auto tmp = qpowmod(mat, k, p);
cout << tmp;
return 0;
}
高斯消元
const int MAXN = 105;
const double EPS = 1e-8;
struct Matrix
{
int r,c;
double ele[MAXN][MAXN];
Matrix(int r,int c):r(r),c(c) { memset(ele,0,sizeof(ele)); }
double &operator()(int r,int c) { return ele[r][c]; }
};
istream &operator>>(istream &is, Matrix &mat)
{
for (int i=0;i<mat.r;i++)
for (int j=0;j<mat.c;j++)
is>>mat(i,j);
return is;
}
ostream &operator<<(ostream &os, Matrix &mat)
{
for(int i=0;i<mat.r;i++)
{
for(int j=0;j<mat.c;j++)
os<<fixed<<setprecision(2)<<mat(i,j)<<' ';
os<<endl;
}
return os;
}
bool gauss(Matrix &mat)
{
for(int i=0;i<mat.r;i++) //遍历行
{
int p = i; //当前系数
for(int j=i+1;j<mat.r;j++) //遍历每行的当前位置
if(abs(mat(p,i))<abs(mat(j,i)))
p = j;
if(abs(mat(p,i))<EPS) return false;
if(p!=i) for(int j=0;j<mat.c;j++)
swap(mat(i,j),mat(p,j));
for(int j=i+1;j<mat.c;j++)
mat(i,j)/=mat(i,i);
for(int j=0;j<mat.r;j++)
{
if(i==j) continue;
for(int k=i+1;k<mat.c;k++)
mat(j,k)-=mat(j,i)*mat(i,k);
}
}
return true;
}
void print(Matrix &mat)
{
for(int i=0;i<mat.r;i++)
cout<<fixed<<setprecision(2)<<mat(i,mat.c-1)<<endl;
}
int main(int argc, char const *argv[])
{
int n;
cin>>n;
static Matrix mat(n,n+1);
cin>>mat;
if(gauss(mat)) print(mat);
else cout<<"No Solution"<<endl;
return 0;
}
数据结构
trie树
struct trie {
int nex[100000][26], cnt;
bool exist[100000]; // 该结点结尾的字符串是否存在
void insert(char *s, int l) { // 插入字符串
int p = 0;
for (int i = 0; i < l; i++) {
int c = s[i] - 'a';
if (!nex[p][c]) nex[p][c] = ++cnt; // 如果没有,就添加结点
p = nex[p][c];
}
exist[p] = 1;
}
bool find(char *s, int l) { // 查找字符串
int p = 0;
for (int i = 0; i < l; i++) {
int c = s[i] - 'a';
if (!nex[p][c]) return 0;
p = nex[p][c];
}
return exist[p];
}
};
树链剖分
const int maxn = 1e5 + 5;
const int maxm = maxn * 2;
struct E
{
int to, next;
} Edge[maxm];
int tot, Head[maxn];
inline void AddEdge(int u, int v)
{
Edge[tot] = (E){v, Head[u]};
Head[u] = tot++;
Edge[tot] = (E){u, Head[v]};
Head[v] = tot++;
}
int mod;
int v[maxn];
int fa[maxn], dep[maxn], siz[maxn], son[maxn];
void dfs1(int u, int f)
{
fa[u] = f;
dep[u] = dep[f] + 1;
siz[u] = 1;
int maxsize = -1;
for (int i = Head[u]; ~i; i = Edge[i].next)
{
int v = Edge[i].to;
if (v == f)
continue;
dfs1(v, u);
siz[u] += siz[v];
if (siz[v] > maxsize)
{
maxsize = siz[v];
son[u] = v;
}
}
}
int tim, dfn[maxn], top[maxn], w[maxn];
void dfs2(int u, int t)
{
dfn[u] = ++tim;
top[u] = t;
w[tim] = v[u];
if (!son[u])
return;
dfs2(son[u], t);
for (int i = Head[u]; ~i; i = Edge[i].next)
{
int v = Edge[i].to;
if (v == fa[u] || v == son[u])
continue;
dfs2(v, v);
}
}
struct Node
{
int l, r, f, val;
} sgt[maxn * 4];
inline int ls(int k) { return k << 1; }
inline int rs(int k) { return k << 1 | 1; }
inline void pushup(int k) { sgt[k].val = (sgt[ls(k)].val + sgt[rs(k)].val) % mod; }
inline void pushdown(int k)
{
sgt[ls(k)].f += sgt[k].f;
sgt[rs(k)].f += sgt[k].f;
sgt[ls(k)].val += (sgt[ls(k)].r - sgt[ls(k)].l + 1) * sgt[k].f % mod;
sgt[rs(k)].val += (sgt[rs(k)].r - sgt[rs(k)].l + 1) * sgt[k].f % mod;
sgt[k].f = 0;
}
void build(int l, int r, int k = 1)
{
sgt[k].l = l, sgt[k].r = r;
if (l == r)
{
sgt[k].val = w[l] % mod;
return;
}
int m = (l + r) >> 1;
build(l, m, ls(k));
build(m + 1, r, rs(k));
pushup(k);
}
void modify(int x, int y, int z, int k = 1)
{
int l = sgt[k].l, r = sgt[k].r;
if (x <= l && y >= r)
{
sgt[k].f += z;
sgt[k].val += (r - l + 1) * z;
sgt[k].val %= mod;
return;
}
if (sgt[k].f)
pushdown(k);
int m = (l + r) >> 1;
if (x <= m)
modify(x, y, z, ls(k));
if (y > m)
modify(x, y, z, rs(k));
pushup(k);
}
int query(int x, int y, int k = 1)
{
int l = sgt[k].l, r = sgt[k].r;
if (x <= l && y >= r)
return sgt[k].val;
if (sgt[k].f)
pushdown(k);
int sum = 0, m = (l + r) >> 1;
if (x <= m)
sum += query(x, y, ls(k));
if (y > m)
sum += query(x, y, rs(k));
return sum % mod;
}
void mchain(int x, int y, int z)
{
z %= mod;
while (top[x] != top[y])
{
if (dep[top[x]] < dep[top[y]])
std::swap(x, y);
modify(dfn[top[x]], dfn[x], z);
x = fa[top[x]];
}
if (dep[x] > dep[y])
std::swap(x, y);
modify(dfn[x], dfn[y], z);
}
int qchain(int x, int y)
{
int ret = 0;
while (top[x] != top[y])
{
if (dep[top[x]] < dep[top[y]])
std::swap(x, y);
ret += query(dfn[top[x]], dfn[x]);
x = fa[top[x]];
}
if (dep[x] > dep[y])
std::swap(x, y);
ret += query(dfn[x], dfn[y]);
return ret % mod;
}
inline void mson(int x, int z)
{
modify(dfn[x], dfn[x] + siz[x] - 1, z);
}
inline int qson(int x)
{
return query(dfn[x], dfn[x] + siz[x] - 1);
}
ST表
//logg预处理log2
void build()
{
for (int i = 1; i <= n; i++) stmax[i][0] = stmin[i][0] = a[i];
for (int j = 1; j <= logg[n]; j++)
{
for (int i = 1; i + (1 << j) - 1 <= n; i++)
{
stmax[i][j] = max(stmax[i][j - 1], stmax[i + (1 << j - 1)][j - 1]);
stmin[i][j] = min(stmin[i][j - 1], stmin[i + (1 << j - 1)][j - 1]);
}
}
}
int getmax(int l, int r)
{
int len = r - l + 1;
int x = logg[len];
return max(stmax[l][x], stmax[r - (1 << x) + 1][x]);
}
int getmin(int l, int r)
{
int len = r - l + 1;
int x = logg[len];
return min(stmin[l][x], stmin[r - (1 << x) + 1][x]);
}
莫队算法
block = n / sqrt(m);
sort(qq, qq + q, [](node a1, node a2) {
if (a1.l / block == a2.l / block)
{
if ((a1.l / block) & 1) return a1.r < a2.r;
return a1.r > a2.r;
}
return a1.l < a2.l;
}); //r++ l++ l-- r--
主席树
求区间第k大
#include <algorithm>
#include <cstdio>
#include <cstring>
using namespace std;
const int maxn = 1e5; // 数据范围
int tot, n, m;
int sum[(maxn << 5) + 10], rt[maxn + 10], ls[(maxn << 5) + 10],
rs[(maxn << 5) + 10];
int a[maxn + 10], ind[maxn + 10], len;
inline int getid(const int &val) { // 离散化
return lower_bound(ind + 1, ind + len + 1, val) - ind;
}
int build(int l, int r) { // 建树
int root = ++tot;
if (l == r) return root;
int mid = l + r >> 1;
ls[root] = build(l, mid);
rs[root] = build(mid + 1, r);
return root; // 返回该子树的根节点
}
int update(int k, int l, int r, int root) { // 插入操作
int dir = ++tot;
ls[dir] = ls[root], rs[dir] = rs[root], sum[dir] = sum[root] + 1;
if (l == r) return dir;
int mid = l + r >> 1;
if (k <= mid)
ls[dir] = update(k, l, mid, ls[dir]);
else
rs[dir] = update(k, mid + 1, r, rs[dir]);
return dir;
}
int query(int u, int v, int l, int r, int k) { // 查询操作
int mid = l + r >> 1,
x = sum[ls[v]] - sum[ls[u]]; // 通过区间减法得到左儿子中所存储的数值个数
if (l == r) return l;
if (k <= x) // 若 k 小于等于 x ,则说明第 k 小的数字存储在在左儿子中
return query(ls[u], ls[v], l, mid, k);
else // 否则说明在右儿子中
return query(rs[u], rs[v], mid + 1, r, k - x);
}
inline void init() {
scanf("%d%d", &n, &m);
for (int i = 1; i <= n; ++i) scanf("%d", a + i);
memcpy(ind, a, sizeof ind);
sort(ind + 1, ind + n + 1);
len = unique(ind + 1, ind + n + 1) - ind - 1;
rt[0] = build(1, len);
for (int i = 1; i <= n; ++i) rt[i] = update(getid(a[i]), 1, len, rt[i - 1]);
}
int l, r, k;
inline void work() {
while (m--) {
scanf("%d%d%d", &l, &r, &k);
printf("%d\n", ind[query(rt[l - 1], rt[r], 1, len, k)]); // 回答询问
}
}
int main() {
init();
work();
return 0;
}
珂朵莉树
骗分。只要是有区间赋值操作的数据结构题都可以用来骗分。
节点保存方法
struct Node_t {
int l, r;
mutable int v;
Node_t(const int &il, const int &ir, const int &iv) : l(il), r(ir), v(iv) {}
inline bool operator<(const Node_t &o) const { return l < o.l; }
};
split
将包含x的区间分为\([l,x)和[x,r]\),返回指向后者的迭代器
auto split(int x) {
if (x > n) return odt.end();
auto it = --odt.upper_bound((Node_t){x, 0, 0});
if (it->l == x) return it;
int l = it->l, r = it->r, v = it->v;
odt.erase(it);
odt.insert(Node_t(l, x - 1, v));
return odt.insert(Node_t(x, r, v)).first;
}
assign
区间赋值
void assign(int l, int r, int v) {
auto itr = split(r + 1), itl = split(l);
odt.erase(itl, itr);
odt.insert(Node_t(l, r, v));
}
其他操作
珂朵莉树在进行求取区间左右端点操作时,必须先 split 右端点,再 split 左端点
void performance(int l, int r) {
auto itr = split(r + 1), itl = split(l);
for (; itl != itr; ++itl) {
// Perform Operations here
}
}
图论
SPFA判负环
bool spfa(int n, int s) {
memset(dis, 63, sizeof(dis));
dis[s] = 0, vis[s] = 1;
q.push(s);
while (!q.empty()) {
int u = q.front();
q.pop(), vis[u] = 0;
for (auto ed : e[u]) {
int v = ed.v, w = ed.w;
if (dis[v] > dis[u] + w) {
dis[v] = dis[u] + w;
cnt[v] = cnt[u] + 1; // 记录最短路经过的边数
if (cnt[v] >= n) return false;
// 在不经过负环的情况下,最短路至多经过 n - 1 条边
// 因此如果经过了多于 n 条边,一定说明经过了负环
if (!vis[v]) q.push(v), vis[v] = 1;
}
}
}
return true;
}
字符串
KMP
int nxt[M];
char s[M],p[M];
//获得next数组
void getnext(char *p,int len){
int i,j;
i=0,j=nxt[0]=-1;
while(i<len){
while(j!=-1&&p[i]!=p[j]) j=nxt[j];
nxt[++i]=++j;
}
}
//返回p在s中第一次出现的位置
void kmp(char *s,char *p){
int len1,len2,i,j;
i=j=0;
len1=strlen(s);
len2=strlen(p);
getnext(p,len2);
while(i<len1){
while(j!=-1&&s[i]!=p[j]) j=nxt[j];
i++,j++;
if(j==len2)
return i-len2+1;
}
return -1;
}
用途
在字符串中查找子串
字符串的周期
统计每个前缀的出现次数
其他
快读
inline int read()
{
int s = 0, w = 1;
char ch = getchar();
while (ch < '0' || ch > '9')
{
if (ch == '-') w = -1;
ch = getchar();
}
while (ch >= '0' && ch <= '9') s = s * 10 + ch - '0', ch = getchar();
return s * w;
}
安全的unordered_map
struct custom_hash
{
static uint64_t splitmix64(uint64_t x)
{
x += 0x9e3779b97f4a7c15;
x = (x ^ (x >> 30)) * 0xbf58476d1ce4e5b9;
x = (x ^ (x >> 27)) * 0x94d049bb133111eb;
return x ^ (x >> 31);
}
size_t operator()(uint64_t x) const
{
static const uint64_t FIXED_RANDOM = chrono::steady_clock::now().time_since_epoch().count();
return splitmix64(x + FIXED_RANDOM);
}
};
unordered_map<int, int, custom_hash> mp;
分数类
struct frac
{
__int128 p,q;
frac(){}
frac(__int128 _p,__int128 _q)
{
if(_q<0) _p=-_p,_q=-_q;
p=_p,q=_q;
}
frac operator +(const frac&rhs)
{
__int128 a,b;
b=q*rhs.q;
a=p*rhs.q+q*rhs.p;
if(b==0) return frac(0,1);
__int128 g=__gcd(a,b);
a/=g;
b/=g;
return frac(a,b);
}
frac operator -(const frac&rhs)
{
__int128 a,b;
b=q*rhs.q;
a=p*rhs.q-q*rhs.p;
if(b==0) return frac(0,1);
__int128 g=__gcd(a,b);
a/=g;
b/=g;
return frac(a,b);
}
frac operator *(const frac&rhs)
{
__int128 a,b;
b=q*rhs.q;
a=p*rhs.p;
if(b==0) return frac(0,1);
__int128 g=__gcd(a,b);
a/=g;
b/=g;
return frac(a,b);
}
frac operator /(const frac&rhs)
{
__int128 a,b;
b=q*rhs.p;
a=p*rhs.q;
if(b==0) return frac(0,1);
__int128 g=__gcd(a,b);
a/=g;
b/=g;
return frac(a,b);
}
bool operator <(const frac&rhs)const
{
return p*rhs.q<rhs.p*q;
}
bool operator >(const frac&rhs)const
{
return p*rhs.q>rhs.p*q;
}
bool operator ==(const frac&rhs)const
{
return !(p*rhs.q<rhs.p*q)&&!(p*rhs.q>rhs.p*q);
}
};
大数类
struct BigIntTiny {
int sign;
std::vector<int> v;
BigIntTiny() : sign(1) {}
BigIntTiny(const std::string &s) { *this = s; }
BigIntTiny(int v) {
char buf[21];
sprintf(buf, "%d", v);
*this = buf;
}
void zip(int unzip) {
if (unzip == 0) {
for (int i = 0; i < (int)v.size(); i++)
v[i] = get_pos(i * 4) + get_pos(i * 4 + 1) * 10 + get_pos(i * 4 + 2) * 100 + get_pos(i * 4 + 3) * 1000;
} else
for (int i = (v.resize(v.size() * 4), (int)v.size() - 1), a; i >= 0; i--)
a = (i % 4 >= 2) ? v[i / 4] / 100 : v[i / 4] % 100, v[i] = (i & 1) ? a / 10 : a % 10;
setsign(1, 1);
}
int get_pos(unsigned pos) const { return pos >= v.size() ? 0 : v[pos]; }
BigIntTiny &setsign(int newsign, int rev) {
for (int i = (int)v.size() - 1; i > 0 && v[i] == 0; i--)
v.erase(v.begin() + i);
sign = (v.size() == 0 || (v.size() == 1 && v[0] == 0)) ? 1 : (rev ? newsign * sign : newsign);
return *this;
}
std::string to_str() const {
BigIntTiny b = *this;
std::string s;
for (int i = (b.zip(1), 0); i < (int)b.v.size(); ++i)
s += char(*(b.v.rbegin() + i) + '0');
return (sign < 0 ? "-" : "") + (s.empty() ? std::string("0") : s);
}
bool absless(const BigIntTiny &b) const {
if (v.size() != b.v.size()) return v.size() < b.v.size();
for (int i = (int)v.size() - 1; i >= 0; i--)
if (v[i] != b.v[i]) return v[i] < b.v[i];
return false;
}
BigIntTiny operator-() const {
BigIntTiny c = *this;
c.sign = (v.size() > 1 || v[0]) ? -c.sign : 1;
return c;
}
BigIntTiny &operator=(const std::string &s) {
if (s[0] == '-')
*this = s.substr(1);
else {
for (int i = (v.clear(), 0); i < (int)s.size(); ++i)
v.push_back(*(s.rbegin() + i) - '0');
zip(0);
}
return setsign(s[0] == '-' ? -1 : 1, sign = 1);
}
bool operator<(const BigIntTiny &b) const {
return sign != b.sign ? sign < b.sign : (sign == 1 ? absless(b) : b.absless(*this));
}
bool operator==(const BigIntTiny &b) const { return v == b.v && sign == b.sign; }
BigIntTiny &operator+=(const BigIntTiny &b) {
if (sign != b.sign) return *this = (*this) - -b;
v.resize(std::max(v.size(), b.v.size()) + 1);
for (int i = 0, carry = 0; i < (int)b.v.size() || carry; i++) {
carry += v[i] + b.get_pos(i);
v[i] = carry % 10000, carry /= 10000;
}
return setsign(sign, 0);
}
BigIntTiny operator+(const BigIntTiny &b) const {
BigIntTiny c = *this;
return c += b;
}
void add_mul(const BigIntTiny &b, int mul) {
v.resize(std::max(v.size(), b.v.size()) + 2);
for (int i = 0, carry = 0; i < (int)b.v.size() || carry; i++) {
carry += v[i] + b.get_pos(i) * mul;
v[i] = carry % 10000, carry /= 10000;
}
}
BigIntTiny operator-(const BigIntTiny &b) const {
if (sign != b.sign) return (*this) + -b;
if (absless(b)) return -(b - *this);
BigIntTiny c;
for (int i = 0, borrow = 0; i < (int)v.size(); i++) {
borrow += v[i] - b.get_pos(i);
c.v.push_back(borrow);
c.v.back() -= 10000 * (borrow >>= 31);
}
return c.setsign(sign, 0);
}
BigIntTiny operator*(const BigIntTiny &b) const {
if (b < *this) return b * *this;
BigIntTiny c, d = b;
for (int i = 0; i < (int)v.size(); i++, d.v.insert(d.v.begin(), 0))
c.add_mul(d, v[i]);
return c.setsign(sign * b.sign, 0);
}
BigIntTiny operator/(const BigIntTiny &b) const {
BigIntTiny c, d;
d.v.resize(v.size());
double db = 1.0 / (b.v.back() + (b.get_pos((unsigned)b.v.size() - 2) / 1e4) +
(b.get_pos((unsigned)b.v.size() - 3) + 1) / 1e8);
for (int i = (int)v.size() - 1; i >= 0; i--) {
c.v.insert(c.v.begin(), v[i]);
int m = (int)((c.get_pos((int)b.v.size()) * 10000 + c.get_pos((int)b.v.size() - 1)) * db);
c = c - b * m, d.v[i] += m;
while (!(c < b))
c = c - b, d.v[i] += 1;
}
return d.setsign(sign * b.sign, 0);
}
BigIntTiny operator%(const BigIntTiny &b) const { return *this - *this / b * b; }
bool operator>(const BigIntTiny &b) const { return b < *this; }
bool operator<=(const BigIntTiny &b) const { return !(b < *this); }
bool operator>=(const BigIntTiny &b) const { return !(*this < b); }
bool operator!=(const BigIntTiny &b) const { return !(*this == b); }
};

浙公网安备 33010602011771号