开头
#include<bits/stdc++.h>
#define eps 1e-9
#define pb push_back
#define fi first
#define se second
#define endl '\n'
#define io ios::sync_with_stdio(false); cin.tie(0); cout.tie(0)
#define db1(x) cout << #x << " = " << x << endl
#define db2(x, y) cout << #x << " = " << x << " " << #y << " = " << y << endl
#define gg cout << "---------------------\n"
#pragma GCC optimize(2) // fast 万一呢
typedef long long ll;
typedef pair<int, int> pii;
// clock_t c1 = clock();
// std::cerr << "Time:" << clock() - c1 << "ms";
// freopen("in.txt","r",stdin);
// freopen("out.txt","w",stdout);
结构体重载小于号
struct node{
int x, id;
// 使用sort排序时按x从大到小排序, 在优先队列中相反
bool operator<(const node &a)const{
return x>a.x;
}
};
快读
inline ll rd() {
ll x=0,f=1; char c=getchar();
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;
}
inline void out(int N){
if (N < 0){
putchar('-');
N = -N;
}
if (N >= 10){
out(N / 10);
}
putchar(N % 10 + '0');
}
数论模板
const ll maxn = 1e5;
ll fac[maxn], facinv[maxn], inv[maxn];
const ll mod = 1e9 + 7;
ll MOD(ll a, ll m) { // 取余
a %= m;
return a >= 0 ? a : a + m;
}
// m为质数时, a的逆元为 a^m-2。
ll inverse(ll a, ll m) { // 求a mod m时的逆元
a = MOD(a, m);
if (a <= 1) return a;
return MOD((1 - inverse(m, a) * m) / a, m);
}
ll gcd(ll x, ll y){
return x%y == 0 ? y:gcd(y, x%y);
}
ll gcd(ll a, ll b) { // 最大公约数
a = abs(a), b = abs(b);
while (b != 0) {
a %= b;
swap (a, b);
}
return a;
}
//扩展欧几里得,求x,y 使得 ax+by=gcd(a,b)
int exgcd(ll a, ll b, ll &x, ll &y) {
if (!b){
x = 1; y = 0;
return a;
}
int d = exgcd(b, a % b, y, x);
y -= (a/b) * x;
return d;
}
//快速幂
ll fast(ll a, ll b, ll mod) {
a %= mod;
if (b < 0)a = inverse (a, mod), b = -b;
ll ans = 1;
while (b) {
if (b & 1)ans = ans * a % mod;
a = a * a % mod;
b /= 2;
}
return ans % mod;
}
ll fac[maxn], facinv[maxn], inv[maxn];
void init() {
fac[0] = 1;
for (int i = 1; i < maxn; ++i)
fac[i] = i * (fac[i - 1]) % mod;
facinv[maxn - 1] = inverse(fac[maxn - 1], mod);
for (int i = maxn - 2; i >= 0; --i){
facinv[i] = facinv[i + 1] * (i + 1) % mod;
}
inv[1] = 1;
for (int i = 2; i < maxn; i++)
inv[i] = (mod - (ll) mod / i * inv[mod % i] % mod);
}
//求组合数 c[n,m],调用之前需要先调用init().
ll combi(int n, int m) {
if (n < 0 || m < 0 || n < m)return 0;
return fac[n] * facinv[m] % mod * facinv[n - m] % mod;
}
欧拉定理 求 a^b mod c
很显然,当b大到一定程度时,利用pow或者快速幂这样的算法是无法在给定时间内求解的,这时我们引入欧拉降幂算法,这个算法的特点就是降低幂方的值而不影响最终结果,使我们解决问题的时间缩短。
公式
其中φ(x)是欧拉函数的值,说简单了就是小于等于n的与n互质的数的个数,比如如果n时质数,则欧拉函数的值为n-1。
// 求单个数的欧拉函数
int phi(int n){
int ans = n;
for(int i = 2; i<=sqrt(n); 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;
}
// 打表求欧拉函数
void euler() {
for(int i=2;i<maxn;i++){
if(!E[i])
for(int j=i;j<maxn;j+=i){
if(!E[j])E[j]=j;
E[j]=E[j]/i*(i-1);
}
}
}
费马小定理
如果p是一个质数,而整数a不是p的倍数,则有a^(p-1)≡1(mod p)。
由费马小定理可以推出 a 的逆元就是 a^(p-2)。采用快速幂求得即可、
//1、线性求逆元
int inv[MAXN];
void INV(int a,int p)//线性求到a的逆元
{
inv[1] = 1;
for (int i=2; i<=a; ++i)
inv[i] = (-(p/i))*inv[p%i]%p;
}
//2、单独求某个值的逆元
int INV(int a, int p)//线性求a的逆元
{
if (a==1) return 1;
return ((-(p/a)*INV(p%a))%p);
}
求正整数约数个数
任意一个正整数 X 都可以表示成若干个质数乘积的形式,即 X = p1^α1 ∗ p2 ^α2 …… ∗ pk ^ αk
则这个数的约数个数为 = (a1 + 1)(a2 + 1)……(ak + 1)
分数取模
(b/a)%p= b∗ a^(p−2) %p
求l-r的异或和
ll fun(ll x){
if(x%4==0) return x;
else if(x%4==1) return 1;
else if(x%4==2) return x+1;
else if(x%4==3) return 0;
}
ll myxor(ll l,ll r){
return fun(r)^fun(l-1);
}
逆元
//1、线性求逆元
int inv[maxn];
void INV(int a,int p)//线性求到a的逆元
{
inv[1] = 1;
for (int i=2; i<=a; ++i)
inv[i] = (-(p/i))*inv[p%i]%p;
}
//2、单独求某个值的逆元
int INV(int a)//线性求a的逆元
{
if (a==1) return 1;
return ((-(p/a)*INV(p%a))%p);
}
素数筛
bool vis[maxn];
int prime[maxn],x;
void isprime(int n) { //埃氏筛
for(int i=2;i<=n;i++){
if(!vis[i]) prime[x++]=i;
for(int j=2;j*i<=n;j++){
vis[i*j]=true;
}
}
}
void oulasai(int n) { //欧拉筛
for(int i=2;i<=n;i++){
if(!vis[i]) prime[x++]=i;
for(int j=0;j<x;j++){
if(i*prime[j]>n) break;
vis[i*prime[j]]=true;
if(i%prime[j]==0) break;
}
}
}
int vis[maxn];
void isprime(int n) { //埃氏筛 求每个数的最小质因数。
for(int i=2;i<=n;i++){
if(!vis[i]) prime[x++]=i, vis[i] = i;
for(int j=2;j*i<=n;j++){
if(vis[i*j] == 0)
vis[i*j] = i;
}
}
}
void oulasai(int n) { //欧拉筛求每个数的最小质因数。
for(int i=2;i<=n;i++){
if(!vis[i]) prime[x++]=i,vis[i] = i;
for(int j=0;j<x;j++){
if(i*prime[j]>n) break;
vis[i*prime[j]]=prime[j];
if(i%prime[j]==0) break;
}
}
}
FFT
const double PI = acos(-1.0);
//定义的复数结构体
struct Complex{
double x, y; //x + yi
Complex(double _x = 0.0, double _y = 0.0){
x = _x;
y = _y;
}
Complex operator-(const Complex &b) const {
return Complex(x - b.x, y - b.y);
}
Complex operator+(const Complex &b) const {
return Complex(x + b.x, y + b.y);
}
Complex operator*(const Complex &b) const {
return Complex(x * b.x - y * b.y, x * b.y + y * b.x);
}
};
void change(Complex y[], int len){
int i, j, k;
for (i = 1, j = len / 2; i < len - 1; i++){
if (i < j)
swap(y[i], y[j]);
k = len / 2;
while (j >= k) {
j -= k; k /= 2;
}
if (j < k) j += k;
}
return;
}
void fft(Complex y[], int len, int on)
{
change(y, len);
for (int h = 2; h <= len; h <<= 1){
Complex wn(cos(-on * 2 * PI / h), sin(-on * 2 * PI / h));
for (int j = 0; j < len; j += h) {
Complex w(1, 0);
for (int k = j; k < j + h / 2; k++) {
Complex u = y[k];
Complex t = w * y[k + h / 2];
y[k] = u + t;
y[k + h / 2] = u - t;
w = w * wn;
}
}
}
if (on == -1){
for (int i = 0; i < len; i++) {
y[i].x /= len;
}
}
}
Complex a[maxn], b[maxn], c[maxn];
int main(){
int s = 1<<20; // s必须为2的幂次
fft(a,s,1);fft(b,s,1);//dft
for(int i = 0; i < s; i++) c[i] = a[i] * b[i];
fft(c,s,-1);
for(int i=0;i<s;i++){ //还原成十进制数
output[i] += (int)(c[i].x+0.5);//注意精度误差
}
}
数列公式
等差数列前n项和
等比数列前n项和:
莫比乌斯反演
const int maxn=1e6+5;
bool vis[maxn];
int prime[maxn],mu[maxn];
void init_mu(int n){//init_mu(n) 就可得到1-n的莫比乌斯函数的值,存在mu中
int cnt=0;
mu[1]=1;
for(int i=2;i<n;i++){
if(!vis[i]){
prime[cnt++]=i;
mu[i]=-1;
}
for(int j=0;j<cnt&&i*prime[j]<n;j++){
vis[i*prime[j]]=1;
if(i%prime[j]==0) {mu[i*prime[j]]=0;break;}
else { mu[i*prime[j]]=-mu[i];}
}
}
}
几何模板
1.求点到线段最短距离
struct P{
llb x, y;
};
//p1、p2组成的线段到点p的最短距离
llb get(P p, P p1, P p2)
{
llb dis = 0.0;
llb dx = p2.x - p1.x;
llb dy = p2.y - p1.y;
// 两直线垂直,向量表示法,转换后公示
llb k = -((p1.x - p.x)*dx + (p1.y - p.y)*dy) / ( dx*dx + dy*dy);
llb footX = k*dx + p1.x;
llb footY = k*dy + p1.y;
//if垂足是否落在线段上
if( footY >= min(p1.y, p2.y) && footY <=max(p1.y, p2.y)
&& footX >= min(p1.x, p2.x) && footX <=max(p1.x, p2.x ) ){
dis = sqrt((footX-p.x)*(footX-p.x) + (footY-p.y)*(footY-p.y));
}
else{
llb dis1 = sqrt((p1.x-p.x)*(p1.x-p.x) + (p1.y-p.y)*(p1.y-p.y));
llb dis2 = sqrt((p2.x-p.x)*(p2.x-p.x) + (p2.y-p.y)*(p2.y-p.y));
dis = ( dis1 < dis2 ? dis1 : dis2 );
}
return dis;
}
最长上升子序
int arr[N], n, lis[N];
// 最长上升子序列 nlongn
int Lis(){
int len = 0;
// 以1为起始
lis[++len] = arr[1];
for(int i = 2; i <= n; i++){
if(arr[i] > lis[len])
lis[++len] = arr[i];
else{
int pos = lower_bound(lis+1, lis+1+len, arr[i])-lis;
lis[pos] = arr[i];
}
}
return len;
}
// 下降
bool cmp(int a, int b){
return a > b;
}
int Lis1(){
memset(lis,0,sizeof(lis));
int len = 0;
// 以1为起始
lis[++len] = arr[1];
for(int i = 2; i <= n; i++){
if(arr[i] < lis[len])
lis[++len] = arr[i];
else{
int pos = lower_bound(lis+1, lis+1+len, arr[i], cmp)-lis;
lis[pos] = arr[i];
}
}
return len;
}
二维差分
//在左上角是 (x1,y1),右下角是 (x2,y2) 的矩形区间每个值都 +c
void updata(int x1, int y1, int x2, int y2, int c){
dif[x1][y1] += c;
dif[x1][y2+1] -=c;
dif[x2+1][y1] -=c;
dif[x2+1][y2+1] += c;
}
// 由原始矩阵得到前缀和矩阵
sum[i][j] = sum[i-1][j] + sum[i][j-1] - sum[i-1][j-1] + arr[i][j];
// 原始矩阵得到差分矩阵
dif[i][j] = arr[i][j] - arr[i-1][j] - arr[i][j-1] + arr[i-1][j-1];
离散化
vector<int> alls; // 存储所有待离散化的值
sort(alls.begin(), alls.end()); // 将所有值排序
alls.erase(unique(alls.begin(), alls.end()), alls.end()); // 去掉重复元素
// 二分求出x对应的离散化的值
int find(ll x) // 找到第一个大于等于x的位置
{
int l = 0, r = alls.size() - 1;
while (l < r){
int mid = l + r >> 1;
if (alls[mid] >= x) r = mid;
else l = mid + 1;
}
return r + 1; // 映射到1, 2, ...n
}
字符串哈希
核心思想:将字符串看成P进制数,P的经验值是131或13331,取这两个值的冲突概率低
小技巧:取模的数用2^64,这样直接用unsigned long long存储,溢出的结果就是取模的结果
typedef unsigned long long ULL;
const int maxn = 2e6+10;
const int P = 13331;
ULL h1[maxn], h2[maxn], p[maxn]; // h[k]存储字符串前k个字母的哈希值, p[k]存储 P^k mod 2^64
// 字符串拼接时注意前面的字符串要乘以对应的 p[后面字符串的个数]
// 初始化
p[0] = 1;
//下标从1开始, 如果是字符串(下标从0开始)使用get时get(l,r)对应[l-1,r-1]
for (int i = 1; i <= str.size(); i ++ ){
h1[i] = h1[i - 1] * P + str[i-1];
p[i] = p[i - 1] * P;
}
// l<r 求回文串时 反方向
for (int i = str.size(); i >= 1; i--){
h2[i] = h2[i + 1] * P + str[i-1];
}
// 计算子串 str[l-1 ~ r-1] 的哈希值
ULL get1(int l, int r){
return h1[r] - h1[l - 1] * p[r - l + 1];
}
// l<r 求回文串时 反方向
// 比如abcde get2(1,3)求的是字符串cba的值。get(3,5)求的是edc的值。
ULL get2(int l, int r){
return h2[l] - h2[r + 1] * p[r - l + 1];
}
// 取模版
const int mod = 1e9+7;
p[0] = 1;
for(int i = 1; i <= str.size(); i++){
h1[i] = (h1[i-1]*P%mod + str[i-1]) % mod;
p[i] = (p[i-1]*P) % mod;
}
for(int i = str.size(); i >= 1; i--){
h2[i] = ( h2[i+1]*P%mod + str[i-1]) % mod;
}
ll get1(int l, int r){
return (h1[r] - (h1[l-1] * p[r-l+1]%mod)%mod + mod) % mod;
}
ll get2(int l, int r){
return (h2[l] - (h2[r+1] * p[r-l+1]%mod)%mod + mod) % mod ;
}
字符串最小表示法
// 获得一个数组的最小表示法
int N = 6; // 数组长度
void get_min(int arr[]){
int w[N*2];
for(int i = 0; i < N*2; i++){
w[i] = arr[i%N];
}
// i 与 j 不能相同
int i = 0, j = 1, k = 0;
while(i<=N&&j<=N){
for(k = 0; k<N&&w[i+k]==w[j+k]; k++);
if(k == N) break;
if(w[i+k] > w[j+k]){
i+=k+1; if(i==j) i++;
}else{
j+=k+1; if(i==j) j++;
}
}
//db2(i,j);
i = min(i, j);
for(int k = 0; k<N; k++,i++){
arr[k] = w[i];
//cout << arr[k] << " ";
}//cout << endl;
}
位运算技巧
& 与 两个位都为1 时,结果才为1
| 或 两个位都为0 时,结果才为0
^ 异或 两个位相同为0,相异为1
~ 取反 0变1,1变0
<< 左移 各二进位全部左移若干位,*2
>> 右移 各二进位全部右移若干位,/2
返回n的最后一位1:lowbit(n) = n & -n
获得第i位的数字: (a>>i)&1 或者 a&(1<<i)
设置第i位为1: a=a|(1<<i) //i从0开始
设置第i位为0: a=a&(~(1<<i))
把第i位取反: a=a^(1<<i)
获取数字二进制位的的个数: __builtin_popcount(int num)
数据结构
单调栈
class MinStack {
public:
/** initialize your data structure here. */
// 最小栈, 可改为最大栈
stack<int> s1, s2;
MinStack() {
}
void push(int x) {
s1.push(x);
if(s2.empty()|| x <= s2.top()){
s2.push(x);
}
}
void pop() {
if(s1.top() == s2.top()) s2.pop();
s1.pop();
}
int top() {
return s1.top();
}
int getMin() {
return s2.top();
}
};
并查集
int find(int x){
return x == fa[x] ? x : (fa[x] = find(fa[x]));
}
void merge(int i, int j){
fa[find(i)] = find(j);
}
Trie树
字符串字典树
int son[N][26], cnt[N], idx;
// 0号点既是根节点,又是空节点
// son[][]存储树中每个节点的子节点
// cnt[]存储以每个节点结尾的单词数量
// 插入一个字符串
void insert(string str){
int p = 0;
for (int i = 0; str[i]; i ++ ){
int u = str[i] - 'a';
if (!son[p][u]) son[p][u] = ++ idx;
p = son[p][u];
}
cnt[p] ++ ;
}
// 查询字符串出现的次数
int query(string str){
int p = 0;
for (int i = 0; str[i]; i ++ ){
int u = str[i] - 'a';
if (!son[p][u]) return 0;
p = son[p][u];
}
return cnt[p];
}
数字字典树(求最大异或和)
int son[N*31][2], idx;
int arr[N];
// 0号点既是根节点,又是空节点, 使用前最好把0点先插入进去
// son[][]存储树中每个节点的子节点
// 插入一个数
void insert(int x){
int p = 0;
// 从最高位开始
for(int i = 31; i >= 0; i--){
int u = x >> i & 1;
if(son[p][u] == 0) son[p][u] = ++idx;
p = son[p][u];
}
}
// 查找
int query(int x){
int p = 0, ans = 0;
for(int i = 31; i >= 0; i--){
int u = x >> i & 1;
if(son[p][!u] == 0){ //与该数相反的数不存在
p = son[p][u];
ans = ans * 2 + u; //计算这个数的值。
}else{
p = son[p][!u];
ans = ans * 2 + !u;
}
}
return ans^x; // 返回异或结果
}
树状数组
单点修改 + 区间查询
ll sum[maxn];
void add(int p, ll x){ //给位置p增加x
while(p <= n) sum[p] += x, p += p & -p;
}
ll ask(ll p){ //求位置p的前缀和
ll res = 0;
while(p) res += sum[p], p -= p & -p;
return res;
}
ll range_ask(int l, int r){ //区间求和
return ask(r) - ask(l - 1);
}
区间修改 + 单点查询
ll sum[maxn];
void add(int p, ll x){ //这个函数用来在树状数组中直接修改
while(p <= n) sum[p] += x, p += p & -p;
}
void range_add(int l, int r, ll x){ //给区间[l, r]加上x
add(l, x), add(r + 1, -x);
}
ll ask(int p){ //单点查询(查询,经过区间修改后该点变化的值)
ll res = 0;
while(p) res += sum[p], p -= p & -p;
return res;
}
区间修改 + 区间查询
ll sum1[maxn], sum2[maxn];
void add(ll p, ll x){
for(int i = p; i <= n; i += i & -i)
sum1[i] += x, sum2[i] += x * p;
}
void range_add(ll l, ll r, ll x){
add(l, x), add(r + 1, -x);
}
ll ask(ll p){
ll res = 0;
for(int i = p; i; i -= i & -i)
res += (p + 1) * sum1[i] - sum2[i];
return res;
}
ll range_ask(ll l, ll r){
return ask(r) - ask(l - 1);
}
求逆序对
int a[maxn];
int lowbit(int x){return x&(-x);}
void update(int p){
while(p<=n) {
a[p] ++;
p+=lowbit(p);
}
}
int getsum(int p){
int res = 0;
while(p)
res += a[p],p -= lowbit(p);
return res;
}
ll res = 0; // a的逆序对个数为:
for(int i=1;i<=n;i++){
update(b[i]+1);
res += i-getsum(b[i]+1);
}
求区间最大值
const int MAXN = 3e5;
int a[MAXN], h[MAXN];
int n, m;
// a数的值, h区间范围的的最大值
int lowbit(int x){
return x & (-x);
}
void updata(int x){
int lx, i;
while (x <= n){
h[x] = a[x];
lx = lowbit(x);
for (i=1; i<lx; i<<=1)
h[x] = max(h[x], h[x-i]);
x += lowbit(x);
}
}
int query(int x, int y){
int ans = 0;
while (y >= x){
ans = max(a[y], ans);
y --;
for (; y-lowbit(y) >= x; y -= lowbit(y))
ans = max(h[y], ans);
}
return ans;
}
二维树状数组
void add(int x,int y,ll v)
{
for(int i=x;i<=n;i+=lowbit(i))
for(int j=y;j<=m;j+=lowbit(j))
{
t1[i][j]+=v;
t2[i][j]+=v*x;
t3[i][j]+=v*y;
t4[i][j]+=v*x*y;
}
}
void real_add(int x1,int y1,int x2,int y2,ll v)
{
add(x1,y1,v);
add(x1,y2+1,-v);
add(x2+1,y1,-v);
add(x2+1,y2+1,v);
}
ll ask(int x, int y)
{
ll res=0;
for(int i=x;i;i-=lowbit(i))
for(int j=y;j;j-=lowbit(j))
res+=(x+1)*(y+1)*t1[i][j]-(y+1)*t2[i][j]-(x+1)*t3[i][j]+t4[i][j];
return res;
}
ll real_ask(int x1,int y1,int x2,int y2)
{
return ask(x2,y2)-ask(x2,y1-1)-ask(x1-1,y2)+ask(x1-1,y1-1);
}
线段树
#include <bits/stdc++.h>
using namespace std;
using ll = long long;
const int MAXN = 1e5 + 5;
ll tree[MAXN << 2], mark[MAXN << 2], n, m, A[MAXN];
void push_down(int p, int len)
{
if (len <= 1) return;
tree[p << 1] += mark[p] * (len - len / 2);
mark[p << 1] += mark[p];
tree[p << 1 | 1] += mark[p] * (len / 2);
mark[p << 1 | 1] += mark[p];
mark[p] = 0;
}
void build(int p = 1, int cl = 1, int cr = n)
{
if (cl == cr) return void(tree[p] = A[cl]);
int mid = (cl + cr) >> 1;
build(p << 1, cl, mid);
build(p << 1 | 1, mid + 1, cr);
tree[p] = tree[p << 1] + tree[p << 1 | 1];
}
ll query(int l, int r, int p = 1, int cl = 1, int cr = n)
{
if (cl >= l && cr <= r) return tree[p];
push_down(p, cr - cl + 1);
ll mid = (cl + cr) >> 1, ans = 0;
if (mid >= l) ans += query(l, r, p << 1, cl, mid);
if (mid < r) ans += query(l, r, p << 1 | 1, mid + 1, cr);
return ans;
}
void update(int l, int r, int d, int p = 1, int cl = 1, int cr = n)
{
if (cl >= l && cr <= r) return void(tree[p] += d * (cr - cl + 1), mark[p] += d);
push_down(p, cr - cl + 1);
int mid = (cl + cr) >> 1;
if (mid >= l) update(l, r, d, p << 1, cl, mid);
if (mid < r) update(l, r, d, p << 1 | 1, mid + 1, cr);
tree[p] = tree[p << 1] + tree[p << 1 | 1];
}
int main()
{
ios::sync_with_stdio(false);
cin >> n >> m;
for (int i = 1; i <= n; ++i)
cin >> A[i];
build();
while (m--)
{
int o, l, r, d;
cin >> o >> l >> r;
if (o == 1)
cin >> d, update(l, r, d);
else
cout << query(l, r) << '\n';
}
return 0;
}
ST表
(求区间最大最小值)预处理 O(nlogn), 查询O(1)
int logn[maxn], fmx[maxn][21], fmi[maxn][21], arr[maxn];
// 初始化st表
fmx[i][0] = fmi[i][0] = arr[i];
for (int i = 1; i <= 20; ++i)
for (int j = 1; j + (1 << i) - 1 <= n; ++j){
fmx[j][i] = max(fmx[j][i - 1], fmx[j + (1 << (i - 1))][i - 1]);
fmi[j][i] = min(fmi[j][i - 1], fmi[j + (1 << (i - 1))][i - 1]);
}
}
//初始化log
void pre(){
logn[1] = 0, logn[2] = 1;
for(int i = 3; i < maxn; i++) logn[i] = logn[i/2]+1;
}
// 求 [l,r]区间最大值
int findmax(int l, int r){
int s = logn[r-l+1];
return max(fmx[l][s], fmx[r-(1<<s)+1][s]);
}
int findmin(int l, int r){
int s = logn[r-l+1];
return min(fmi[l][s], fmi[r-(1<<s)+1][s]);
}
后缀数组nlogn
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N=100010;
int wa[N],wb[N],wv[N],wss[N],rak[N],height[N],cal[N],n,sa[N];
char s[N];
int cmp(int *r,int a,int b,int l)
{return r[a]==r[b]&&r[a+l]==r[b+l];}
void da(int *r,int *sa,int n,int M) {
int i,j,p,*x=wa,*y=wb,*t;
for(i=0;i<M;i++) wss[i]=0;
for(i=0;i<n;i++) wss[x[i]=r[i]]++;
for(i=1;i<M;i++) wss[i]+=wss[i-1];
for(i=n-1;i>=0;i--) sa[--wss[x[i]]]=i;
for(j=1,p=1;p<n;j*=2,M=p) {
for(p=0,i=n-j;i<n;i++) y[p++]=i;
for(i=0;i<n;i++) if(sa[i]>=j) y[p++]=sa[i]-j;
for(i=0;i<n;i++) wv[i]=x[y[i]];
for(i=0;i<M;i++) wss[i]=0;
for(i=0;i<n;i++) wss[wv[i]]++;
for(i=1;i<M;i++) wss[i]+=wss[i-1];
for(i=n-1;i>=0;i--) sa[--wss[wv[i]]]=y[i];
for(t=x,x=y,y=t,p=1,x[sa[0]]=0,i=1;i<n;i++)
x[sa[i]]=cmp(y,sa[i-1],sa[i],j)?p-1:p++;
}
return;
}
void calheight(int *r,int *sa,int n) {
int i,j,k=0;
for(i=1;i<=n;i++) rak[sa[i]]=i;
for(i=0;i<n;height[rak[i++]]=k)
for(k?k--:0,j=sa[rak[i]-1];r[i+k]==r[j+k];k++);
for(int i=n;i;i--)rak[i]=rak[i-1],sa[i]++;
}
int main(){
int cas=1;
while(~scanf("%s",s+1)){
n=strlen(s+1);
for(int i=1;i<=n;i++)
cal[i]=s[i];
cal[n+1]=0;
//200是字符ASCII码大小范围
da(cal+1,sa,n+1,200);
calheight(cal+1,sa,n);
for(int i=1;i<=n;i++) {
printf("%d ",height[i]);
}puts("");
for(int i=1;i<=n;i++) {
printf("%d ",sa[i]);
}puts("");
for(int i=1;i<=n;i++) {
printf("%d ",rak[i]);
}puts("");
}
}
莫队
const int maxn = 1e5+10;
struct ques{
int id,l,r;
}q[100010];//结构体存每个询问,注意要记录询问顺序以便输出
const int block=1300;// 分的块大小 或者为sqrt(n)
int a[maxn*2],ans[maxn],res=0;//a数组数据,ans存各个询问答案,res当前左右端点答案
int vis[maxn];
void add(int pos){
//视情况,略..这里为统计区间不同数的个数
if(vis[a[pos]] == 0) res++;
vis[a[pos]]++;
}
void del(int pos){
//视情况,略..
if(vis[a[pos]] == 1) res--;
vis[a[pos]]--;
}
//排序比较函数
bool cmp(ques a,ques b){
return a.l/block==b.l/block?a.r<b.r:a.l/block<b.l/block;
}
int main(){
int n, m, nowl, nowr;
memset(vis,0,sizeof(vis));
for(int i=1; i<=n; i++)
scanf("%d", &a[i]);
for(int i=1; i<=m; i++){
scanf("%d%d", &q[i].l,&q[i].r); //读入数据及询问
q[i].id=i;
}
res = 0; nowl=0; nowr=0; //当前左右端点
sort(q+1, q+m+1, cmp); //给询问排序
for(int i=1; i<=m; i++){
while(nowl<q[i].l) del(nowl++);
while(nowl>q[i].l) add(--nowl);
while(nowr<q[i].r) add(++nowr);
while(nowr>q[i].r) del(nowr--);
ans[q[i].id]=res;//存下该次询问答案
}
// 输出答案
for(int i = 1; i <= m; i++) printf("%d\n",ans[i]);
}
图论:
堆优化迪杰斯特拉
const int maxn = 2e5;
struct Node {
int pos;
int dis;
Node() {}
Node(int p, int d) {
pos = p;
dis = d;
}
};
// 由于优先队列默认为大顶堆,所以重载小于号要用 a.dis > b.dis,来起到小顶堆的作用
bool operator<(const Node &a, const Node &b) {
return a.dis > b.dis;
}
int dis[maxn]; // 距离
vector<Node> G[maxn]; // 存图
priority_queue<Node> que; //优先队列
bool st[maxn]; // 判断是否遍历过
void dij(int s) { // s 起始点
// 初始化距离
fill(dis, dis + n + 1, INT_MAX);
dis[s] = 0;
que.push(Node(s, 0));
while(!que.empty()) {
Node tmp = que.top();
que.pop();
if (st[tmp.pos]) continue;
st[tmp.pos] = true;
// 更新节点 tmp 的邻接节点,若邻接节点被更新,则将节点和 dis 加入小顶堆
int len = G[tmp.pos].size();
for(int i = 0; i < len; ++i) {
int pos = G[tmp.pos][i].pos;
int d = G[tmp.pos][i].dis;
if(dis[pos] > tmp.dis + d) {
dis[pos] = tmp.dis + d;
que.push(Node(pos, dis[pos]));
}
}
}
}
//堆优化,优先级队列 复杂度O(E*log(E))
struct node{
ll v,c;
node(ll _v=0,ll _c=0):v(_v),c(_c){}
bool operator < (const node &r)const {return c>r.c;}
};
struct Edge{
ll v,cost;
Edge (ll _v=0,ll _cost=0):v(_v),cost(_cost){}
};
//vector<Edge>E[maxn];
bool vis[maxn];
ll dis[maxn];
void dij(vector<Edge>E[maxn], int n, int start)
{
memset(vis,false,sizeof (vis));
for(int i=0;i<=n;i++) dis[i]=inf;
priority_queue<node>q;
while(!q.empty()) q.pop();
dis[start]=0;
q.push(node(start,0));
node x;
while(!q.empty())
{
x=q.top(); q.pop();
ll u=x.v;
if(vis[u])continue;
vis[u]=true;
for(unsigned int i=0;i<E[u].size();i++){
ll v=E[x.v][i].v;
ll cost=E[u][i].cost;
if(!vis[v]&&dis[v]>dis[u]+cost){
dis[v]=dis[u]+cost;
q.push(node(v,dis[v]));
}
}
}
}
SPFA
vector<node> G[maxn];
bool inqueue[maxn];
int dist[maxn];
void Init() {
for(int i = 0 ; i < maxn ; ++i){
G[i].clear();
dist[i] = INF;
}
}
int SPFA(int s,int e) {
int v1,v2,weight;
queue<int> Q;
memset(inqueue,false,sizeof(inqueue)); // 标记是否在队列中
memset(cnt,0,sizeof(cnt)); // 加入队列的次数
dist[s] = 0;
Q.push(s); // 起点加入队列
inqueue[s] = true; // 标记
while(!Q.empty()){
v1 = Q.front();
Q.pop();
inqueue[v1] = false; // 取消标记
for(int i = 0 ; i < G[v1].size() ; ++i){ // 搜索v1的链表
v2 = G[v1][i].vex;
weight = G[v1][i].weight;
if(dist[v2] > dist[v1] + weight){ // 松弛操作
dist[v2] = dist[v1] + weight;
if(inqueue[v2] == false){ // 再次加入队列
inqueue[v2] = true;
//cnt[v2]++; // 判负环
//if(cnt[v2] > n) return -1;
Q.push(v2);
} } }
}
return dist[e];
}
/*
不断的将s的邻接点加入队列,取出不断的进行松弛操作,直到队列为空
如果一个结点被加入队列超过n-1次,那么显然图中有负环
*/
Kruskal
/*
第一步:点、边、加入vector,把所有边按从小到大排序
第二步:并查集部分 + 下面的code
*/
void Kruskal() {
ans = 0;
for (int i = 0; i<len; i++) {
if (Find(edge[i].a) != Find(edge[i].b)) {
Union(edge[i].a, edge[i].b);
ans += edge[i].len;
}
}
}
Prim
/*
|适用于 稠密图 求最小生成树|
|堆优化版,时间复杂度:O(elgn)|
*/
struct node {
int v, len;
node(int v = 0, int len = 0) :v(v), len(len) {}
bool operator < (const node &a)const { // 加入队列的元素自动按距离从小到大排序
return len> a.len;
}
};
vector<node> G[maxn];
int vis[maxn];
int dis[maxn];
void init() {
for (int i = 0; i<maxn; i++) {
G[i].clear();
dis[i] = INF;
vis[i] = false;
}
}
int Prim(int s) {
priority_queue<node>Q; // 定义优先队列
int ans = 0;
Q.push(node(s,0)); // 起点加入队列
while (!Q.empty()) {
node now = Q.top(); Q.pop(); // 取出距离最小的点
int v = now.v;
if (vis[v]) continue; // 同一个节点,可能会推入2次或2次以上队列,这样第一个被标记后,剩下的需要直接跳过。
vis[v] = true; // 标记一下
ans += now.len;
for (int i = 0; i<G[v].size(); i++) { // 开始更新
int v2 = G[v][i].v;
int len = G[v][i].len;
if (!vis[v2] && dis[v2] > len) {
dis[v2] = len;
Q.push(node(v2, dis[v2])); // 更新的点加入队列并排序
}
}
}
return ans;
}
染色法判断二分图
int bipartite(int s) {
int u, v;
queue<int>Q;
color[s] = 1;
Q.push(s);
while (!Q.empty()) {
u = Q.front();
Q.pop();
for (int i = 0; i < G[u].size(); i++) {
v = G[u][i];
if (color[v] == 0) {
color[v] = -color[u];
Q.push(v);
}
else if (color[v] == color[u])
return 0;
}
}
return 1;
}
匈牙利算法
时间复杂度是 O(nm), n表示点数,m 表示边数
int n1, n2; // n1表示第一个集合中的点数,n2表示第二个集合中的点数
int h[N], e[M], ne[M], idx; // 邻接表存储所有边,匈牙利算法中只会用到从第一个集合指向第二个集合的边,所以这里只用存一个方向的边
int match[N]; // 存储第二个集合中的每个点当前匹配的第一个集合中的点是哪个
bool st[N]; // 表示第二个集合中的每个点是否已经被遍历过
bool find(int x){
for (int i = h[x]; i != -1; i = ne[i]) {
int j = e[i];
if (!st[j]){
st[j] = true;
if (match[j] == 0 || find(match[j])) {
match[j] = x;
return true;
}
}
}
return false;
}
// 求最大匹配数,依次枚举第一个集合中的每个点能否匹配第二个集合中的点
int res = 0;
for (int i = 1; i <= n1; i ++ ){
memset(st, false, sizeof st);
if (find(i)) res ++ ;
}
几何
凸包模板
struct lp {
ll x, y;
bool operator<(const lp&b) const {
if(x == b.x) return y < b.y;
return x < b.x;
}
} cw[maxn],R[maxn];
ll cross(lp a, lp b, lp c) {
return ((b.x - a.x) * (c.y - a.y) - (c.x - a.x) * (b.y - a.y));
}
// 求的是按逆时针方向得到的 R[]是返回的结果
int convex(int n) {
int tn = 0, k;
sort(cw, cw + n);
for(int i = 0; i < n; i++) {
while(tn > 1 && cross(R[tn - 1], cw[i], R[tn - 2]) <= 0) tn--;
R[tn++] = cw[i];
}
k = tn;
for(int i = n - 2; i >= 0; i--) {
while(tn > k && cross(R[tn - 1], cw[i], R[tn - 2]) <= 0) tn--;
R[tn++] = cw[i];
}
if(n > 1) tn--;
return tn;
}