ACM板子


板子:

int run(int year) {  if((year%4==0&&year%100!=0)||(year%400==0)) 	return 1; else 	return 0; }

**蓝桥杯函数:**

全排列函数:next_permutation(a.begin(), a.end())

stoi(s) 将字符串s转为数字

二分查找:binary_search(arr[],arr[]+size , indx)


__builtin_popcount(n) :n的⼆进制中1的个数
__builtin_ffs(n):返回最低的非 0 位的下标,下标从 1 还是计数,0未定义
__builtin_clz(n):返回从最高位开始连续的 0 的个数
__builtin_ctz(n):返回从最低位开始的连续的 0 的个数;如果传入 0 则行为未定义
    

1.判断一个数字x二进制下第i位是不是等于1。(最低第1位)

方法:if(((1<<(i−1))&x)>0) 将1左移i-1位,相当于制造了一个只有第i位 上是1,其他位上都是0的二进制数。然后与x做与运算,如果结果>0, 说明x第i位上是1,反之则是0。

2.将一个数字x二进制下第i位更改成1。

方法:x=x|(1<<(i−1)) 证明方法与1类似。

3.将一个数字x二进制下第i位更改成0。

方法:x=x&~(1<<(i−1))

4.把一个数字二进制下最靠右的第一个1去掉。

方法:x=x&(x−1)
    
    按位或运算符(|)

运算规则:参加运算的两个数只要两个数中的一个为1,结果就为1。

即 0 | 0= 0 , 1 | 0= 1 , 0 | 1= 1 , 1 | 1= 1 。

异或运算符(^)

运算规则:参加运算的两个数,如果两个相应位为“异”(值不同),则该位结果为1,否则为0。

即 0 ^ 0=0 , 0 ^ 1= 1 , 1 ^ 0= 1 , 1 ^ 1= 0 。

按位与运算符(&)

运算规则:只有两个数的二进制同时为1,结果才为1,否则为0。(负数按补码形式参加按位与运算)

即 0 & 0= 0 ,0 & 1= 0,1 & 0= 0, 1 & 1= 1。

fill()函数参数:fill(first,last,val); 、//可以相当于memset赋值 ,赋值随意。
ceil() 函数向上舍入为最接近的整数。
**提示**:如需向下舍入为最接近的整数,请查看 floor() 函数。
**提示**:如需对浮点数进行四舍五入,请查看 round() 函数。

初始化:

#pragma GCC optimize(2)
#include <bits/stdc++.h>
#define IOS ios::sync_with_stdio(false);cin.tie(0);cout.tie(0);
#define CAS int cas;cin>>cas;while(cas--)
#define  mem(a,b) memset(a,b,sizeof(a))
#define f(i,l,h) for(int i=l;i<=h;++i)
#define eps 1e-9
#define pi acos(-1.0)
#define pb push_back
#define se second
#define fi first
const int maxn=500005;
const int INF = 0x3f3f3f3f;
typedef pair<int, int>  pii;
typedef long long  ll;

using namespace std;

快读:

ll read(){ll x=0,f=1;char ch=getchar();while(ch<'0'||ch>'9'){if(ch=='-') f=-1;ch=getchar();}while(ch>='0'&&ch<='9'){x=(x<<1)+(x<<3)+(ch^48);ch=getchar();}return x*f;}
inline void Prin(ll x){if(x < 0){putchar('-');x = -x;}if(x > 9) Prin(x / 10);putchar(x % 10 + '0');}
usin

inline int read(){//快读,可快了
	int x=0,f=0;char ch=getchar();
	while(!isdigit(ch))f|=ch=='-',ch=getchar();
	while(isdigit(ch))x=x*10+(ch^48),ch=getchar();
	return f?-x:x;
}

快速幂:

ll quick(ll a,ll b,ll m) {
       ll ans=1;
      while(b) {
           if(b&1)
            ans=(a*ans)%m;
          a=(a*a)%m;
          b>>=1;
    }
 return ans;
}

GCD/LCM:

ll gcd(ll a, ll b) {
    return b == 0 ? a : gcd(b, a % b);
}

ll lcm(ll a, ll b) {
    return a / gcd(a, b) * b;
}

组合数:

#define mod 1000000007
ll fac[maxn],inv[maxn];

void init(){
	fac[0] = inv[0] = 1;
	fac[1] = inv[1] = 1;
	for(int i = 2; i <= maxn - 9; ++i)// 一定要从2开始
		fac[i] = fac[i-1] * i % mod,
		inv[i] = (mod - mod / i) * inv[mod % i] % mod;
	for(int i = 1; i <= maxn - 9; ++i)
		inv[i] = inv[i] * inv[i-1] % mod;
}


ll c(int n,int m){
    if(m>n||m<0) return 0;
        return fac[n]*inv[m]%mod*inv[n-m]%mod;
}

欧拉筛:

const int maxx = 1e6+10;
int prime[maxx+10];
bool vis[maxx+10];
int k=0;

void init(){
    vis[0]=vis[1]=1;
    for(int i=2;i<=maxx;i++){
        if(vis[i]==0)prime[++k]=i,vis[i]=1;
        for(int j=1;i*prime[j]<=maxx&&j<=k;j++){
            vis[i*prime[j]]=1;
            if(i%prime[j]==0)break;
        }
    }
}

大整数质因数分解:

const int MAXN = 10000005 ;
#define int64_t long long
int64_t mulEx(int64_t a , int64_t b , int64_t Mod) {///logn快速乘
    if(!a) return 0 ;
    int64_t ans(0) ;
    while(b)
    {
		if(b & 1) ans = (ans + a) % Mod;
		a <<= 1 ;
		a %= Mod ;
		b >>= 1 ;
    }
    return ans ;
}

int64_t powEx(int64_t base , int64_t n , int64_t Mod)
{///快速幂
    int64_t ans(1) ;
    while(n)
    {
        if(n & 1) ans = mulEx(ans , base , Mod) ;
        base = mulEx(base , base , Mod) ;
        n >>= 1 ;
    }
    return ans ;
}

bool check(int64_t a , int64_t d , int64_t n)
{
    if(n == a) return true ;
    while(~d & 1) d >>= 1 ;
    int64_t t = powEx(a , d , n) ;
    while(d < n - 1 && t != 1 && t != n - 1)
    {
        t = mulEx(t , t , n) ;
        d <<= 1 ;
    }
    return (d & 1) || t == n - 1 ;
}

bool isP(int64_t n)
{ ///判断大数是否是质数
    if(n == 2) return true ;
    if(n < 2 || 0 == (n & 1)) return false ;
    static int p[5] = {2 , 3 , 7 , 61 , 24251} ;
    for(int i = 0 ; i < 5 ; ++ i) if(!check(p[i] , n - 1 , n)) return false ;
    return true ;
}

int64_t gcd(int64_t a , int64_t b)
{
    if(a < 0) return gcd(-a , b) ;
    return b ? gcd(b , a - b * (a / b)) : a ;
}

int64_t Pollard_rho(int64_t n , int64_t c)
{///大数分解质因数
    int64_t i = 1 , k = 2 , x = rand() % n , y = x ;
    while(true)
    {
        x = (mulEx(x , x , n) + c) % n ;
        int64_t d = gcd(y - x , n) ;
        if(d != 1 && d != n) return d ;
        if(y == x) return n ;
        if(++ i == k)
        {
            y = x ;
            k <<= 1 ;
        }
    }
}

vector<int64_t>Fac, factCnt ;
///Fac存的是质因子,大小不一定按照顺序,有重复
void factorization(int64_t n)
{
    if(n==1) return ;
    if(isP(n))
    {
        Fac.push_back(n) ;
        return ;
    }
    int64_t p(n) ;
    while(p >= n) p = Pollard_rho(p , rand() % (n - 1) + 1) ;
    factorization(p) ;
    factorization(n / p) ;
}

map<int64_t , int64_t> factMap ;
///遍历map的first表示因子,second表示次数

void getFactor(int64_t x)
{///不用判断是否是质数,但是比较费时间
 /**因此最好先判断一下是否是质数**/
	srand(time(0)) ;
	//factCnt = 0 ;
	factMap.clear() ;
	factorization(x) ;
	for(int i = 0; i < Fac.size(); ++i) ++ factMap[Fac[i]] ;
}

分解质因数:

//求每个数最小质因子
int cnt=0,lim=1e6+5;

void getprime(){
    int cnt=0;
    isprime[1]=1;
    for(int i=2;i<=lim;i++){
        if(isprime[i]==0){
            prime[++cnt]=i,isprime[i]=i;
        }
        for(int j=1;j<=cnt&&i*prime[j]<=lim;j++){
            isprime[i*prime[j]]=prime[j];
            if(i%prime[j]==0){
                break;
            }
        }
    }
}

//分解质因数
 while(x!=1) {
     ++pz[isprime[x]];
     x/=isprime[x];
 }

求 (1e12) 内质数:

#include <bits/stdc++.h>
using namespace std;
using ll = long long;
//通过知道前面的 n^1/3 的质数可以推断后面n^2/3的质数所以可以适当减小
const int N  = 9e3;
const int M  = 2;         //为了减小内存可以不过是质数
const int PM = 2 * 3 * 5; //为了减小内存可以不过要按质数减小如去掉17
ll n;
bool np[N];
int prime[N], pi[N];
int phi[PM + 1][M + 1], sz[M + 1];

int getprime() {
    int cnt = 0;
    np[0] = np[1] = true;
    pi[0] = pi[1] = 0;
    for (int i = 2; i < N; ++i) {
        if (!np[i]) prime[++cnt] = i;
        pi[i] = cnt;
        for (int j = 1; j <= cnt && i * prime[j] < N; ++j) {
            np[i * prime[j]] = true;
            if (i % prime[j] == 0) break;
        }
    }
    return cnt;
}

void init() {
    getprime();
    sz[0] = 1;
    for (int i = 0; i <= PM; ++i) phi[i][0] = i;
    for (int i = 1; i <= M; ++i) {
        sz[i] = prime[i] * sz[i - 1];
        for (int j = 1; j <= PM; ++j) phi[j][i] = phi[j][i - 1] - phi[j / prime[i]][i - 1];
    }
}

int sqrt2(ll x) {
    ll r = (ll)sqrt(x - 0.1);
    while (r * r <= x) ++r;
    return int(r - 1);
}

int sqrt3(ll x) {
    ll r = (ll)cbrt(x - 0.1);
    while (r * r * r <= x) ++r;
    return int(r - 1);
}

ll getphi(ll x, int s) {
    if (s == 0) return x;
    if (s <= M) return phi[x % sz[s]][s] + (x / sz[s]) * phi[sz[s]][s];
    if (x <= prime[s] * prime[s]) return pi[x] - s + 1;
    if (x <= prime[s] * prime[s] * prime[s] && x < N) {
        int s2x = pi[sqrt2(x)];
        ll ans  = pi[x] - (s2x + s - 2) * (s2x - s + 1) / 2;
        for (int i = s + 1; i <= s2x; ++i) ans += pi[x / prime[i]];
        return ans;
    }
    return getphi(x, s - 1) - getphi(x / prime[s], s - 1);
}

ll getpi(ll x) {
    if (x < N) return pi[x];
    ll ans = getphi(x, pi[sqrt3(x)]) + pi[sqrt3(x)] - 1;
    for (int i = pi[sqrt3(x)] + 1, ed = pi[sqrt2(x)]; i <= ed; ++i) ans -= getpi(x / prime[i]) - i + 1;
    return ans;
}

ll lehmer_pi(ll x) { //小于等于n的素数有多少个
    if (x < N) return pi[x];
    int a  = (int)lehmer_pi(sqrt2(sqrt2(x)));
    int b  = (int)lehmer_pi(sqrt2(x));
    int c  = (int)lehmer_pi(sqrt3(x));
    ll sum = getphi(x, a) + (ll)(b + a - 2) * (b - a + 1) / 2;
    for (int i = a + 1; i <= b; i++) {
        ll w = x / prime[i];
        sum -= lehmer_pi(w);
        if (i > c) continue;
        ll lim = lehmer_pi(sqrt2(w));
        for (int j = i; j <= lim; j++) sum -= lehmer_pi(w / prime[j]) - (j - 1);
    }
    return sum;
}

int main() {
    ios_base::sync_with_stdio(false), cin.tie(0);
    init();
    while (cin >> n && n) cout << lehmer_pi(n) << "\n";
    return 0;
}

KMP:

int nex[N];

void getnex(string s){
    int k=-1;
    int i=0;
    nex[0]=-1;
    int len=s.size();
    while(i<len){
        if(k==-1||s[i]==s[k]){
            i++,k++;
            nex[i]=k;
        }else
           k=nex[k];
        //cout<<i<<endl;
    }
}


int KMP(string s,string p){
   int lens=s.length();
   int lenp=p.length();
   int nex[max];
   getnex(nex,p);
   int i=0;j=0;
   while(i<lens&&j<lenp){
     if(j==-1||s[i]==p[j]){
       i++;j++;
     }
     else
       j=nex[j];
   }
    if(j>=lenp) return i-lenp;
    else return -1;
}

最小生成树:

#include<cstdio>
#include<iostream>
#include<algorithm>
#define N 100010
using namespace std;
int n, m, ans = 0, cnt = 0;
int head[N], fa[N];
struct edge{
	int u, v, w;
}e[N << 1];
inline int read(){
	int s = 0;
	char ch = getchar();
	while(ch < '0' || ch > '9') ch = getchar();
	while(ch >= '0' && ch <= '9'){s = s * 10 + ch - '0'; ch = getchar();}
	return s;
}
bool cmp(edge a, edge b){return a.w < b.w;}
int find(int x){	//!!!非常重要,并查集模板 
	if(x != fa[x]) fa[x] = find(fa[x]);
	return fa[x];
}
void Kruskal(){	//Kruskal模板 
	sort(e + 1, e + m + 1, cmp);
	for(int i = 1; i <= n; i++) fa[i] = i;
	for(int i = 1; i <= m; i++){
		int x = find(e[i].u), y = find(e[i].v);
		if(x == y) continue;	//如果两条边已经联通那么跳过 
		ans += e[i].w;
		fa[x] = y;	//将两个点合并 
		if(++cnt == n - 1) break; //当n - 1条边时结束 
	}
}
int main(){
	n = read(); m = read();
	for(int i = 1; i <= m; i++){
		int x, y, z; 
		x = read(); y = read(); z = read();
		e[i].u = x; e[i].v = y; e[i].w = z;
	}
	Kruskal();
	printf("%d\n", ans);
	return 0;
}

最短路

//Dijkstra算法
#define ll long long
const int N=200005;
const int inf=0x3f3f3f3f;
struct node{
    int v;
    int c;
    bool operator <(const node &r)
     const{
         return c>r.c;
     }
};
struct edge{
    int v,val;
};
vector<edge> g[N];
int vis[N];
ll dis[N];

void dij(int n,int st){
    memset(vis,0,sizeof(vis));
    for(int i=1;i<=n;i++) dis[i]=inf;
    priority_queue<node> q;
    while(!q.empty())
        q.pop();
    dis[st]=0;
    q.push({st,0});
    node tmp;
    while(!q.empty()){
        tmp=q.top();
        q.pop();
        int u=tmp.v;
        if(vis[u]) continue;
        vis[u]=1;
        for(int i=0;i<g[u].size();i++){
            int v=g[u][i].v;
            int val=g[u][i].val;
            if(!vis[v]&&dis[v]>dis[u]+val){
                dis[v]=dis[u]+val;
                q.push({v,dis[v]});
            }
        }
    }
}
 


//spfa算法

const int N=100005;
struct node{
    int to,val;
}e;

vector<node> g[N];

int vis[N],dis[N];
int n,m,st,ed;

queue<int>q;

void spfa(int x){
     for(int i=1;i<=n;i++) dis[i]=inf;
     dis[x]=0;
     q.push(x);
     vis[x]=1;
     cnt[st]=1;
     while(!q.empty()){
        int u=q.front();q.pop();vis[u]=0;
        for(int i=0;i<g[u].size();i++){
            int v=g[u][i].to;
            int w=g[u][i].val;
            if(dis[v]>dis[u]+w){
                dis[v]=dis[u]+w;
                if(!vis[v]){
                    vis[v]=1,q.push(v);
                    if(++cnt[v]>n)return false;  //cnt[i] 为入队列次数,用来判定是否存在负环回路
                }
            }
        }
     }

}


LCA查找最近祖先

//Tarjan算法
Tarjan(u,fa)//marge和find为并查集合并函数和查找函数
{
    for each(u,v)    //访问所有u子节点v
    {
        if(v==fa||vis[v]) continue;
        Tarjan(v,u);        //继续往下遍历
        marge(u,v);    //合并v到u上
        标记v被访问过;
    }
    for each(u,e)    //访问所有和u有询问关系的e
    {
        如果e被访问过;
        u,e的最近公共祖先为find(e);
    }
}
//倍增 ST
int d[N],p[N][22],w[N][22]; //w[i][j]: i为起点 到2^j内 单边最大权值
/*
void dfs(int u,int f){
    for(int i=0;i<ans[u].size();i++){
        int v=ans[u][i].v;
        if(v==f) continue;
        d[v]=d[u]+1;
        p[v][0]=u;
        w[v][0]=ans[u][i].val;
        dfs(v,u);
    }
}

void in(){
    for(int j=1;(1<<j)<=n;j++)
     for(int i=1;i<=n;i++)
      p[i][j]=p[p[i][j-1]][j-1],w[i][j]=max(w[u][i-1],w[p[i][j-1]][j-1]);
}
*/
void dfs(int u,int f){
    d[u]=d[f]+1;
    p[u][0]=f;
    for(int i=1;(1<<i) <=d[u];i++){
        p[u][i]=p[p[u][i-1]][i-1];
        w[u][i]=max(w[u][i-1],w[p[u][i-1]][i-1]);
    }
    for(int i=0;i<ans[u].size();i++){
        int v=ans[u][i].v;
        if(v==f) continue;
        w[v][0]=ans[u][i].val;
        dfs(v,u);
    }
}
int lca(int a,int b){
    if(d[a]>d[b]) swap(a,b);
    for(int i=20;i>=0;i--){
        if(d[a]<=d[b]-(1<<i)) b=p[b][i];
    }
    if(a==b) return a;
    for(int i=20;i>=0;i--){
        if(p[a][i]==p[b][i]) continue;
        else{
            a=p[a][i]; b=p[b][i];
        }
    }
    return p[a][0];
}


二分图匹配

#include <stdio.h>
#include <vector>
#include <string.h>
using namespace std;
const int N = 205;

vector<int>G[N];
int n,m;
int vis[N],pre[N]; //标记  存男朋友
int c[N]; //颜色

//用染色法判断这个图是否是二分图
bool Dfs(int u,int colour){
    c[u] = colour;
    for(int i = 0;i < G[u].size();i++){
        int v = G[u][i];
        if(c[v] == 0 && Dfs(v,3-colour)){
            return true;
        }else if(c[v] != 3-colour) {
            printf("No\n");
            return true;
        }
    }
    return false;
}
//用匈牙利算法求出最大匹配数
bool dfs(int u){
    for(int i = 0;i <G[u].size();i++){
        int v = G[u][i];
        if(!vis[v]){
            vis[v] = 1;
            if(!pre[v] || dfs(pre[v])){//如果前一个可以移动也行
                pre[v] = u;//记录前一个
                return true;
            }
        }
    }
    return false;
}
void solve(){
    for(int i = 1;i <= n;i++){    //染色
        if(c[i] == 0 && Dfs(i,1)){
            return;
        }
    }
    int ans = 0;
    for(int i = 1;i <= n;i++){ //匈牙利
        memset(vis,0,sizeof vis);
        if(dfs(i)) ans++;
    }
    printf("%d\n",ans);
}
void init(){
    for(int i = 0;i < N;i++){
        G[i].clear();
    }
    memset(pre,0,sizeof pre);
    memset(c,0,sizeof c);
}
int main(){
    while(~scanf("%d%d",&n,&m)){
        init();
        while(m--){
            int x,y;
            scanf("%d%d",&x,&y);
            G[x].push_back(y);
        }
        solve();
    }
    return 0;
}


RMQ区间最小值:

void rmq_init(){
    for(int i=1;i<=N;i++)
        dp[i][0]=arr[i];//初始化
    for(int j=1;(1<<j)<=N;j++)
        for(int i=1;i+(1<<j)-1<=N;i++)
            dp[i][j]=min(dp[i][j-1],dp[i+(1<<j-1)][j-1]);
}


//查询
int rmq(int l,int r){
    int k=log2(r-l+1);
    return min(dp[l][k],dp[r-(1<<k)+1][k]);
}

01字典树:

const int MAXN = 5e5 + 5;
int nxt[MAXN*32][2], cnt;
int MAXBIT = 31,val[MAXN];
void init(){
    nxt[0][0] = nxt[0][1] = 0;
    cnt = 1;
}
void add(int n){
    int cur = 0;
    for (int i = MAXBIT; i >= 0; --i) {
        int bit = (n >> i) & 1;       
        if (!nxt[cur][bit]) {
            nxt[cnt][0] = nxt[cnt][1] = 0;
            nxt[cur][bit] = cnt++;
        }
        cur = nxt[cur][bit];
    }
    val[cur] = n;
}
int que_mx(int x) {
    int u = 0;
    for (int i = MAXBIT; i >= 0; i--) {
        int bit = ((x >> i) & 1);
        if (nxt[u][bit ^ 1]) u = nxt[u][bit ^ 1];
        else u = nxt[u][bit];
    }
    return val[u]^x;
}
int que_mi(int x) {
    int u = 0;
    for (int i = MAXBIT; i >= 0; i--) {
        int bit = ((x >> i) & 1);
        if (nxt[u][bit]) u = nxt[u][bit];
        else u = nxt[u][bit ^ 1];
    }
    return val[u]^x;
}


字典树:

int trie[N][26];
int cnt[N];
int id;

void insert(string s)
{
	int p = 0;
	for (int i = 0; i < s.size(); i++)
	{
		int x = s[i] - 'a';
		if (trie[p][x] == 0) trie[p][x] = ++id;
		p = trie[p][x];
	}
	cnt[p]++;
}

int  find(string s)
{
	int p = 0;
	for (int i = 0; i < s.size(); i++)
	{
		int x = s[i] - 'a';
		if (trie[p][x] == 0)return 0;
		p = trie[p][x];
	}
	return cnt[p];
}

线段树:

//定义
#define maxn 100007  //元素总个数
#define ls l,m,rt<<1
#define rs m+1,r,rt<<1|1
int Sum[maxn<<2],Add[maxn<<2];//Sum求和,Add为懒惰标记 
int A[maxn],n;//存原数组数据下标[1,n] 

/*建树*/

//PushUp函数更新节点信息 ,这里是求和
void PushUp(int rt){Sum[rt]=Sum[rt<<1]+Sum[rt<<1|1];}

//Build函数建树 
void Build(int l,int r,int rt){ //l,r表示当前节点区间,rt表示当前节点编号
	if(l==r) {//若到达叶节点 
		Sum[rt]=A[l];//储存数组值 
		return;
	}
	int m=(l+r)>>1;
	//左右递归 
	Build(l,m,rt<<1);
	Build(m+1,r,rt<<1|1);
	//更新信息 
	PushUp(rt);
}

/*点修改*/

void Update(int L,int C,int l,int r,int rt){//l,r表示当前节点区间,rt表示当前节点编号
	if(l==r){//到叶节点,修改 
		Sum[rt]+=C;
		return;
	}
	int m=(l+r)>>1;
	//根据条件判断往左子树调用还是往右 
	if(L <= m) Update(L,C,l,m,rt<<1);
	else       Update(L,C,m+1,r,rt<<1|1);
	PushUp(rt);//子节点更新了,所以本节点也需要更新信息 
} 

/*区间修改*/
void Update(int L,int R,int C,int l,int r,int rt){//L,R表示操作区间,l,r表示当前节点区间,rt表示当前节点编号 
	if(L <= l && r <= R){//如果本区间完全在操作区间[L,R]以内 
		Sum[rt]+=C*(r-l+1);//更新数字和,向上保持正确
		Add[rt]+=C;//增加Add标记,表示本区间的Sum正确,子区间的Sum仍需要根据Add的值来调整
		return ; 
	}
	int m=(l+r)>>1;
	PushDown(rt,m-l+1,r-m);//下推标记
	//这里判断左右子树跟[L,R]有无交集,有交集才递归 
	if(L <= m) Update(L,R,C,l,m,rt<<1);
	if(R >  m) Update(L,R,C,m+1,r,rt<<1|1); 
	PushUp(rt);//更新本节点信息 
} 


/*区间查询*/

//下推标记
void PushDown(int rt,int ln,int rn){
	//ln,rn为左子树,右子树的数字数量。 
	if(Add[rt]){
		//下推标记 
		Add[rt<<1]+=Add[rt];
		Add[rt<<1|1]+=Add[rt];
		//修改子节点的Sum使之与对应的Add相对应 
		Sum[rt<<1]+=Add[rt]*ln;
		Sum[rt<<1|1]+=Add[rt]*rn;
		//清除本节点标记 
		Add[rt]=0;
	}
}

int Query(int L,int R,int l,int r,int rt){//L,R表示操作区间,l,r表示当前节点区间,rt表示当前节点编号
	if(L <= l && r <= R){
		//在区间内,直接返回 
		return Sum[rt];
	}
	int m=(l+r)>>1;
	//下推标记,否则Sum可能不正确
	PushDown(rt,m-l+1,r-m); 
	
	//累计答案
	int ANS=0;
	if(L <= m) ANS+=Query(L,R,l,m,rt<<1);
	if(R >  m) ANS+=Query(L,R,m+1,r,rt<<1|1);
	return ANS;
} 


最长上升子序列:

int a[MAXX],dp[MAXX]; // a数组为数据,dp[i]表示长度为i+1的LIS结尾元素的最小值

int main()
{
    int n;
    while(cin>>n)
    {
        for(int i=0; i<n; i++)
        {
            cin>>a[i];
            dp[i]=INF; // 初始化为无限大
        }
        int pos=0;    // 记录dp当前最后一位的下标
        dp[0]=a[0];   // dp[0]值显然为a[0]
        for(int i=1; i<n; i++){
            if(a[i]>dp[pos])    // 若a[i]大于dp数组最大值,则直接添加
                dp[++pos] = a[i];
            else    // 否则找到dp中第一个大于等于a[i]的位置,用a[i]替换之。
                dp[lower_bound(dp,dp+pos+1,a[i])-dp]=a[i];  // 二分查找
        }
        cout<<pos+1<<endl;
    }
    return 0;
}


最长公共子序列:

for(int i=1;i<=n;i++) cin>>a[i],mp[a[i]]=i;
    for(int j=1;j<=n;j++) cin>>b[j];
    int tot=0;dp[0]=mp[b[1]];
    for(int i=2;i<=n;i++){
        if(dp[tot]<mp[b[i]])  dp[++tot]=mp[b[i]];
        else{
            int pos=lower_bound(dp,dp+tot+1,mp[b[i]])-dp;
            dp[pos]=mp[b[i]];
        }
    }
    cout<<tot<<endl;

01背包

for i=1..N
  for j=V..v[i]//不用到0,因为若小于v[i],v[i]这个物品肯定·放不进去;肯定取上一个.
  p[j]=MAX(p[j-volume[i]]+value[i],p[j]);

中国剩余定理模板

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

/*中国剩余定理模板*/
ll ai[maxn],bi[maxn];
//扩展欧几里得:一定存在整数 x, y 满足等式 a * x + b * y = gcd(a,b)
ll exgcd(ll a,ll b,ll &x,ll &y)
{
    if(b==0){ x=1, y=0; return a;}
    ll gcd=exgcd(b,a%b,x,y);
    ll tp=x;
    x=y, y=tp-a/b*y;
    return gcd;
}
ll mult(ll a,ll b,ll mod){
    long long res=0;
    while(b>0){
        if(b&1) res=(res+a)%mod;
        a=(a+a)%mod;
        b>>=1;
    }
    return res;
}
ll excrt(){
    ll x,y;
    ll ans=bi[1],M=ai[1];
    for(int i=2;i<=n;++i){
        ll a=M,b=ai[i],c=(bi[i]-ans%ai[i]+ai[i])%ai[i];
        ll gcd=exgcd(a,b,x,y);
        x=mult(x,c/gcd,b/gcd);
        ans+=x*M;
        M*=b/gcd;
        ans=(ans%M+M)%M;
    }
    return (ans%M+M)%M;
}
int main(){
    cin>>n;
    for(int i=1;i<=n;++i)
        cin>>ai[i]>>bi[i];
    cout<<excrt();
}

Lucas定理。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

扩展Lucas定理

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

typedef long long ll;
//快速幂
ll quick_mod(ll a,ll b,ll m){
    ll ans=1ll;
    while(b){
        if(b&1) ans=ans*a%m;
        b>>=1;
        a=a*a%m;
    }
    return ans;
}
//扩展欧几里得求逆元
ll exgcd(ll a,ll b,ll &x,ll &y){
    if(a%b==0){
        x=0ll;y=1ll;
        return b;
    }
    ll v,tx,ty;
    v=exgcd(b,a%b,tx,ty);
    x=ty;
    y=tx-a/b*ty;
    return v;
}

//
ll inv(ll a,ll p){
    if(!a) return 0ll;
    ll x,y;
    exgcd(a,p,x,y);
    x=(x%p+p)%p;
    return x;
}

ll Mul(ll n,ll pi,ll pk){
    if(!n) return 1ll;
    ll ans=1ll;
    for(ll i=2;i<=pk;i++)
        if(i%pi) ans=ans*i%pk;
    ans=quick_mod(ans,n/pk,pk);
    for(ll i=2;i<=n%pk;i++){
        if(i%pi) ans=ans*i%pk;
    }
    return ans*Mul(n/pi,pi,pk)%pk;
}

ll exlucas(ll n,ll m,ll p,ll pi,ll pk){
    if(m>n) return 0ll;
    ll a=Mul(n,pi,pk),b=Mul(m,pi,pk),c=Mul(n-m,pi,pk);
    ll k=0ll,ans=0ll;
    for(ll i=n;i;i/=pi) k+=i/pi;
    for(ll i=m;i;i/=pi) k-=i/pi;
    for(ll i=n-m;i;i/=pi) k-=i/pi;
    ans=a*inv(b,pk)%pk*inv(c,pk)%pk*quick_mod(pi,k,pk)%pk;
    return ans*(p/pk)%p*inv(p/pk,pk)%p;     //中国剩余定理  a[i]*M*x  余数*其他个个素数的乘积*x
}

int main()
{
    ll n,m,p,ans=0;
    while(cin>>n>>m>>p){
        for(ll x=p,i=2;i<=p;i++){
            if(x%i==0){
                ll pk=1ll;
                while(x%i==0) pk*=i,x/=i;
                ans=(ans+exlucas(n,m,p,i,pk))%p;
            }
        }
        cout<<ans<<endl;
        ans=0;
    }
    return 0;
}


hash+二分 求最长回文串

#pragma GCC optimize(2)
using namespace std;
 
typedef long long ll;
const int MAXLEN = 1000005;
const int MAXNUM = 35;
int strnum=0;		//字符串的数量
//正序和逆序的字符串
string s1, s2;
//结果
int result[MAXNUM] = { 0 };
//进制数
ll p = 10000019;
//mod
ll mod = 1000000007;
//H数组,H[i]表示字符串前i个字符的子串的hash值
ll H1[MAXLEN] = { 0 }, H2[MAXLEN] = { 0 };
//powp[i]表示p的i次方
ll powp[MAXLEN] = { 0 };
 
//计算字符串的H数组
void calH(ll* H,string& str) {
	H[0] = str[0];
	for (int i = 1; i < str.size(); i++) {
		H[i] = (H[i - 1] * p + str[i]) % mod;
	}
}
 
//打表,预先求出p的幂次
void getpowp() {
	powp[0] = 1;
	for (int i = 1; i < MAXLEN; i++) {
		powp[i] = (powp[i - 1] * p)%mod;
	}
}
 
//计算子串的哈希值
ll calsubH(ll* H,int i,int j){
	//对i为0的情况特殊处理
	if (i == 0)
		return H[j];
	else
		return ((H[j] - H[i - 1] * powp[j - i + 1]) % mod + mod) % mod;
}
 
//比较两个子串是否相等
bool cmp(int i1,int j1,int i2,int j2) {
	return calsubH(H1, i1, j1) == calsubH(H2, i2, j2);	
}
 
//对字符串进行预处理,每两个字符之间插入一个'#'
void initstring(string& tmp,int& len) {
	len = 2 * len - 1;
	char* ch = new char[len + 1];
	for (int i = 0; i < len; i++) {
		if (i % 2 == 0)	ch[i] = tmp[i / 2];
		else    ch[i] = '#';
	}
	ch[len] = '\0';
	tmp = string(ch);
}
 
//计算以第i个字符为中心时,回文子串的最大长度
//字符串s1,字符串s2为其反序
int calbyi(int i,int len) {
	//搜索区间的最小值、最大值
	int lb = 0, rb = min(i, len - i-1);	
	//字串的半径(不包括中心点),注意必须初始化应对i=0的情况
	int k=0;
	
	//使用二分法求解
	while (rb > lb) {
		//注意半径要大于等于1,否则会在第一个分支中死循环
		k = (lb + rb) / 2+1;
		//正、反序字串中要比较的子串的开始位置
		int b1 = i - k, b2 = len - k - (i + 1);
		//判断子串哈希值是否相等
		if (cmp(b1,b1+k-1,b2,b2+k-1)) {
			//以i为中心,k为半径的子串是回文子串,可以向两边拓展(用二分的方法)
			//所以修改区间的最小值,表示半径最少有k个字符
			lb = k;
		}
		else {
			//以i为中心,k为半径的子串不是回文子串,所以向中心收缩半径
			//修改区间的最大值,表示半径要小于k个字符
			rb = k - 1;
		}		
	}
 
	//求除掉'#'后的回文子串的长度,注意这里半径不能用k,因为上面退出循环时可能k的值大于正确半径
	//要根据中心字符的类型来判断
	//中心为'#',回文子串为偶数长度
	if (s1[i] == '#') {
		return ((lb + 1) / 2) * 2;
	}
	//中心为英文字符,回文串为奇数长度
	else{
		return 2*(lb/2) + 1;
	}
}
 
//计算字符串s1的最长回文字串长度
int cal(int len) {
	int maxlen=1;	//回文串的最大长度
	//遍历所有点,将其作为中心点求解
	for (int i = 0; i < len; i++) {
		maxlen = max(calbyi(i,len), maxlen);
	}
	return maxlen;
}
 
 
int main(void) {
	ios::sync_with_stdio(false);
	getpowp();		//打表
 
	//给s1,s2预留空间来优化
	s1.reserve(MAXLEN);
	s2.reserve(MAXLEN);
 
	//对所有字符串求解其最长回文子串长度
	string inputstr;
	int strnum = 0;
	int len = 0;
	while(true) {
		getline(cin, s1);
		if (s1 == "END")		break;
		//原始字符串和反序字符串
		len = s1.size();
		initstring(s1,len);		//给字符之间插入'#'
		s2 = s1;
		reverse(s2.begin(), s2.end());
		//计算两个字符串的H数组
		calH(H1, s1);
		calH(H2, s2);
		//求解
		result[++strnum]=cal(len);
	}
	
	//输出结果
	for (int i = 1; i <= strnum; i++) {
		cout << "Case " << i << ": " << result[i] << endl;
	}
}

尼姆博弈模型

尼姆博弈模型,大致上是这样的:
有3堆各若干个物品,两个人轮流从某一堆取任意多的物品,规定每次至少取1个,多者不限(但不能超过一堆),最后取光者得胜。

所以取到(0,0,0,0)就获胜了 ---- 异或和不等零 先手必胜

莫队算法

/*一般来说,如果可以在 O(1) 内从 [l,r] 的答案转移到 [l-1,r] 、 [l+1,r] 、 [l,r-1] 、 [l,r+1] 这四个与之紧邻的区间的答案,则可以考虑使用莫队
转移分为两种情况,往区间里添数,或者往区间里删数,所以可以写成两个函数:del() , add()
*/
int ll,rr,bs;
lll res=0;

struct node{
    int l,r;
    int id;
}p[N];

bool cmp(node a,node b){
    if(a.l/bs == b.l/bs) return a.r < b.r;
    return a.l < b.l;
}

lll cal(lll x){
    return 1ll*x*(x-1)/2;
}

void del(int x){
    cnt[x&1][a[x]]--;
    res -= (cnt[x&1][a[x]]);
}

void add(int x){
    res += (cnt[x&1][a[x]]);
    cnt[x&1][a[x]]++;
}

void slove(int i){
    while(ll < p[i].l) del(ll++);
    while(ll > p[i].l) add(--ll);
    while(rr < p[i].r) add(++rr);
    while(rr > p[i].r) del(rr--);
    ans[p[i].id] = cal(p[i].r - p[i].l + 1) - res;
}

signed main(){
   // ios::sync_with_stdio(false);cin.tie(0);cout.tie(0);
    scanf("%lld%lld", &n, &q);
    bs = sqrt(n);
    for(int i = 1; i <= n; i++) {
       scanf("%lld", &a[i]);
        a[i]--;
        a[i] = a[i] ^ a[i-1];
    }
    for(int i = 1; i <= q; i++){
         scanf("%lld%lld", &p[i].l, &p[i].r);
        p[i].l--; //本题用的是异或和的前缀和 所以求[l,r]区间时 是用到 [l-1,r]
        p[i].id = i;
    }
    sort(p + 1, p + 1 + q, cmp);
    ll = 1, rr = 0;
    for(int i = 1; i <= q; i++){
        slove(i);
    }
    for(int i = 1; i <= q; i++) 
        printf("%lld\n",ans[i]);
    return 0; 
}

权值线段树

/*
权值线段树一般用于维护一段区间的数出现的次数,从它的定义来看,它可以快速计算出一段区间的数的出现次数。

在实际应用中,我们使用权值线段树查询区间第K大的值。

以数的大小为区间 数x要在[1,n]内

tree[rt] 插入数x 找到x在树中位置rt  次数数组tree[rt]++
*/

//单点更新
//类似基础线段树,递归到叶子节点+1然后回溯
void add(int l, int r, int rt, int x){
    if (l == r) tree[rt]++;
    else{
        int mid = (l + r) >> 1;
        if (x <= mid) add(l, mid, rt << 1, x);
        else add(mid + 1, r, rt << 1 | 1, x);
        tree[rt] = tree[rt << 1] + tree[rt << 1 | 1];
    }
}


//查询第K大
int query_kth(int pos, int l, int r, int k){ //如果K比右儿子大,就说明第K大的数在左子树中。然后,K要减去右子节点的值。
    if(l == r) return l;
    int mid = (l + r) >> 1, right = tree[pos << 1 | 1];
    if(k <= right) return query(pos << 1 | 1, mid + 1, r, k);
    else return query(pos << 1, l, mid, k - right);
}
//查询第K小
int query_kth(int pos, int l, int r, int k){
    if(l == r) return l;
    int mid = (l + r) >> 1, left = tree[pos << 1];
    if(k <= left) return query(pos << 1, l, mid, k - left);
    else return query(pos << 1 | 1, mid + 1, r, k);
}

//查询某个数出现次数
int find(int l, int r, int v, int x){
    if (l == r) return tree[v];
    else{
        int mid = (l + r) >> 1;
        if (x <= mid) return find(l, mid, v << 1, x);
        else return find(mid + 1, r, v << 1 | 1, x);
    }
}

//查询一段区间数出现的次数
int find(int l, int r, int v, int x, int y){
    if (l == x && r == y) return tree[v];
    else{
        int mid = (l + r) >> 1;
        if (y <= mid) return find(l, mid, v << 1, x, y);
        else if (x > mid) return find(mid + 1, r, v << 1 | 1, x, y);
        else return find(l, mid, v << 1, x, mid) + find(mid + 1, r, v << 1 | 1, mid + 1, y);
    }
}

//区间离散化
int get_discretization(){
    for (int i = 1; i <= n; ++i) a[i] = read(), b[i] = a[i]; //读入数据
    sort(b + 1, b + n + 1);
    int len = unique(b + 1, b + n + 1) - b - 1;
    for (int i = 1; i <= n; ++i){
        int pos = lower_bound(b + 1, b + len + 1, a[i]) - b;
        a[i] = pos;
    } //离散化
}

回文树

1.len[i]表示编号为i的节点表示的回文串的长度(一个节点表示一个回文串)

2.next[i][c]表示编号为i的节点表示的回文串在两边添加字符c以后变成的回文串的编号(和字典树类似)。

3.fail[i]表示节点i失配以后跳转不等于自身的节点i表示的回文串的最长后缀回文串(和AC自动机类似)。

4.cnt[i]表示节点i表示的本质不同的串的个数(建树时求出的不是完全的,最后count()函数跑一遍以后才是正确的)

5.num[i]表示以节点i表示的最长回文串的最右端点为回文串结尾的回文串个数。

6.last指向新添加一个字母后所形成的最长回文串表示的节点。

7.S[i]表示第i次添加的字符(一开始设S[0] = -1(可以是任意一个在串S中不会出现的字符))。

8.p表示添加的节点个数。

9.n表示添加的字符个数。


map< pair<int,int> ,int> mp,ct;
int has1[M],has2[M];
int bas1[M],bas2[M];
int p1,p2;
const int mod1=3145739;//如有误差手动修改
const int mod2=6291469;

void init_base(){
	p1=2333;//如有误差手动修改
	p2=17;
	bas1[0]=bas2[0]=1;
	// MXN=3e5+7
	for(int i=1;i<M;++i){//多串hash可以这块预处理
		bas1[i]=(bas1[i-1]*p1)%mod1;
		bas2[i]=(bas2[i-1]*p2)%mod2;
	}
}

void init_hash(string s){
	has1[0]=has2[0]=0;
	int len=s.size()-1;
	for(int i=1;i<=len;++i){
		has1[i]=(has1[i-1]*p1+s[i])%mod1;
		has2[i]=(has2[i-1]*p2+s[i])%mod2;   
	}
}

int gethash1(int l,int r){
	return ((has1[r]-has1[l-1]*bas1[r-l+1])%mod1+mod1)%mod1;
}
int gethash2(int l,int r){
	return ((has2[r]-has2[l-1]*bas2[r-l+1])%mod2+mod2)%mod2;
}


struct Palindromic_Tree {
	int next[MAXN][N] ;//next指针,next指针和字典树类似,指向的串为当前串两端加上同一个字符构成
	int fail[MAXN] ;//fail指针,失配后跳转到fail指针指向的节点
	int cnt[MAXN] ;
	int num[MAXN] ;
	int len[MAXN] ;//len[i]表示节点i表示的回文串的长度
	int S[MAXN] ;//存放添加的字符
	int last ;//指向上一个字符所在的节点,方便下一次add
	int n ;//字符数组指针
	int p ;//节点指针
    int ans = 0;
	int newnode ( int l ) {//新建节点
		for ( int i = 0 ; i < N ; ++ i ) next[p][i] = 0 ;
		cnt[p] = 0 ;
		num[p] = 0 ;
		len[p] = l ;
		return p ++ ;
	}
 
	void init () {//初始化
		p = 0 ;
		newnode (  0 ) ;
		newnode ( -1 ) ;
		last = 0 ;
		n = 0 ;
        ans = 0;
		S[n] = -1 ;//开头放一个字符集中没有的字符,减少特判
		fail[0] = 1 ;
	}
 
	int get_fail ( int x ) {//和KMP一样,失配后找一个尽量最长的
		while ( S[n - len[x] - 1] != S[n] ) x = fail[x] ;
		return x ;
	}
 
	void add ( int c,int f ) {
		 //cout<<c<<endl;
		S[++ n] = c ;
		int cur = get_fail ( last ) ;//通过上一个回文串找这个回文串的匹配位置
		if ( !next[cur][c] ) {//如果这个回文串没有出现过,说明出现了一个新的本质不同的回文串
			int fl=n-len[cur]-1;	//找到回文串开头	                     
			int hs1=gethash1(fl,f);
			int hs2=gethash2(fl,f);
            if(ct[{hs1,hs2}]==0) mp[{hs1,hs2}]++,ct[{hs1,hs2}]=1;
            int now = newnode ( len[cur] + 2 ) ;//新建节点
			fail[now] = next[get_fail ( fail[cur] )][c] ;//和AC自动机一样建立fail指针,以便失配后跳转
			next[cur][c] = now ;
			num[now] = num[fail[now]] + 1 ;
		}
		last = next[cur][c] ;
		 cnt[last]++;
	}
    
	void count () {
		for ( int i = p - 1 ; i >= 0 ; -- i ) cnt[fail[i]] += cnt[i] ;
		//父亲累加儿子的cnt,因为如果fail[v]=u,则u一定是v的子回文串!
	}
} ;

  • 11
    点赞
  • 11
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
1 字符串处理 5 1.1 KMP . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 5 1.2 e-KMP . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 8 1.3 Manacher . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 8 1.4 AC 自动机 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 9 1.5 后缀数组 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 11 1.5.1 DA . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 11 1.5.2 DC3 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 14 1.6 后缀自动机 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 15 1.6.1 基本函数 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 15 1.6.2 例题 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 16 1.7 字符串 hash . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 23 2 数学 25 2.1 素数 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 25 2.1.1 素数筛选(判断 <MAXN 的数是否素数) . . . . . . . . . . . . . . . . 25 2.1.2 素数筛选(筛选出小于等于 MAXN 的素数) . . . . . . . . . . . . . . . 25 2.1.3 大区间素数筛选(POJ 2689) . . . . . . . . . . . . . . . . . . . . . . . 25 2.2 素数筛选和合数分解 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 27 2.3 扩展欧几里得算法(求 ax+by=gcd 的解以及逆元) . . . . . . . . . . . . . . . 27 2.4 求逆元 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 28 2.4.1 扩展欧几里德法 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 28 2.4.2 简洁写法 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 28 2.4.3 利用欧拉函数 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 28 2.5 模线性方程组 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 28 2.6 随机素数测试和大数分解 (POJ 1811) . . . . . . . . . . . . . . . . . . . . . . . 29 2.7 欧拉函数 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 32 2.7.1 分解质因素求欧拉函数 . . . . . . . . . . . . . . . . . . . . . . . . . . . 32 2.7.2 筛法欧拉函数 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 32 2.7.3 求单个数的欧拉函数 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 32 2.7.4 线性筛(同时得到欧拉函数和素数表) . . . . . . . . . . . . . . . . . . 32 2.8 高斯消元(浮点数) . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 33 2.9 FFT . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 34 2.10 高斯消元法求方程组的解 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 37 2.10.1 一类开关问题,对 2 取模的 01 方程组 . . . . . . . . . . . . . . . . . . . 37 2.10.2 解同余方程组 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 39 2.11 整数拆分 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 41 2.12 求 A B 的约数之和对 MOD 取模 . . . . . . . . . . . . . . . . . . . . . . . . . . 43 2.13 莫比乌斯反演 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 44 2.13.1 莫比乌斯函数 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 44 2.13.2 例题:BZOJ2301 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 44 2.14 Baby-Step Giant-Step . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 46 2.15 自适应 simpson 积分 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 46 2.16 斐波那契数列取模循环节 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 46 2.17 原根 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 50 2.18 快速数论变换 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 51 2.18.1 HDU4656 卷积取模 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 51 2.19 其它公式 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 54 2.19.1 Polya . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 54 kuangbin 1 ACM Template of kuangbin 3 数据结构 56 3.1 划分树 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 56 3.2 RMQ . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 57 3.2.1 一维 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 57 3.2.2 二维 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 57 3.3 树链剖分 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 59 3.3.1 点权 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 59 3.3.2 边权 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 61 3.4 伸展树(splay tree) . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 64 3.4.1 例题:HDU1890 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 64 3.4.2 例题:HDU3726 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 67 3.5 动态树 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 72 3.5.1 SPOJQTREE . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 72 3.5.2 SPOJQTREE2 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 75 3.5.3 SPOJQTREE4 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 78 3.5.4 SPOJQTREE5 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 82 3.5.5 SPOJQTREE6 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 84 3.5.6 SPOJQTREE7 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 87 3.5.7 HDU4010 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 91 3.6 主席树 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 95 3.6.1 查询区间多少个不同的数 . . . . . . . . . . . . . . . . . . . . . . . . . . 95 3.6.2 静态区间第 k 大 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 97 3.6.3 树上路径点权第 k 大 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 98 3.6.4 动态第 k 大 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 102 3.7 Treap . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 105 3.8 KD 树 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 108 3.8.1 HDU4347 K 近邻 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 108 3.8.2 CF44G . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 111 3.8.3 HDU4742 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 114 3.9 替罪羊树 (ScapeGoat Tree) . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 116 3.9.1 CF455D . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 116 3.10 动态 KD 树 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 120 3.11 树套树 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 123 3.11.1 替罪羊树套 splay . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 123 4 图论 130 4.1 最短路 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 130 4.1.1 Dijkstra 单源最短路 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 130 4.1.2 Dijkstra 算法 + 堆优化 . . . . . . . . . . . . . . . . . . . . . . . . . . . 130 4.1.3 单源最短路 bellman_ford 算法 . . . . . . . . . . . . . . . . . . . . . . . 131 4.1.4 单源最短路 SPFA . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 132 4.2 最小生成树 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 133 4.2.1 Prim 算法 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 133 4.2.2 Kruskal 算法 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 134 4.3 次小生成树 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 135 4.4 有向图的强连通分量 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 136 4.4.1 Tarjan . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 136 4.4.2 Kosaraju . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 137 4.5 图的割点、桥和双连通分支的基本概念 . . . . . . . . . . . . . . . . . . . . . . . 138 4.6 割点与桥 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 139 4.6.1 模板 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 139 kuangbin 2 ACM Template of kuangbin 4.6.2 调用 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 141 4.7 边双连通分支 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 143 4.8 点双连通分支 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 144 4.9 最小树形图 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 147 4.10 二分图匹配 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 149 4.10.1 邻接矩阵(匈牙利算法) . . . . . . . . . . . . . . . . . . . . . . . . . . 149 4.10.2 邻接表(匈牙利算法) . . . . . . . . . . . . . . . . . . . . . . . . . . . 150 4.10.3 Hopcroft-Karp 算法 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 151 4.11 二分图多重匹配 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 152 4.12 二分图最大权匹配(KM 算法) . . . . . . . . . . . . . . . . . . . . . . . . . . 153 4.13 一般图匹配带花树 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 154 4.14 一般图最大加权匹配 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 157 4.15 生成树计数 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 159 4.16 最大流 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 161 4.16.1 SAP 邻接矩阵形式 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 161 4.16.2 SAP 邻接矩阵形式 2 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 162 4.16.3 ISAP 邻接表形式 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 163 4.16.4 ISAP+bfs 初始化 + 栈优化 . . . . . . . . . . . . . . . . . . . . . . . . . 165 4.16.5 dinic . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 166 4.16.6 最大流判断多解 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 168 4.17 最小费用最大流 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 169 4.17.1 SPFA 版费用流 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 169 4.17.2 zkw 费用流 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 170 4.18 2-SAT . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 172 4.18.1 染色法(可以得到字典序最小的解) . . . . . . . . . . . . . . . . . . . . 172 4.18.2 强连通缩点法(拓扑排序只能得到任意解) . . . . . . . . . . . . . . . . 173 4.19 曼哈顿最小生成树 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 177 4.20 LCA . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 179 4.20.1 dfs+ST 在线算法 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 179 4.20.2 离线 Tarjan 算法 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 181 4.20.3 LCA 倍增法 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 184 4.21 欧拉路 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 186 4.21.1 有向图 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 186 4.21.2 无向图 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 188 4.21.3 混合图 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 189 4.22 树分治 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 192 4.22.1 点分治 -HDU5016 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 192 4.22.2 * 点分治 -HDU4918 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 195 4.22.3 链分治 -HDU5039 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 199 5 搜索 205 5.1 Dancing Links . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 205 5.1.1 精确覆盖 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 205 5.1.2 可重复覆盖 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 207 5.2 八数码 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 209 5.2.1 HDU1043 反向搜索 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 209 6 动态规划 212 6.1 最长上升子序列 O(nlogn) . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 212 6.2 背包 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 212 6.3 插头 DP . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 213 kuangbin 3 ACM Template of kuangbin 6.3.1 HDU 4285 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 213 7 计算几何 218 7.1 二维几何 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 218 7.2 三维几何 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 238 7.3 平面最近点对 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 242 7.4 三维凸包 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 243 7.4.1 HDU4273 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 243 8 其他 249 8.1 高精度 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 249 8.2 完全高精度 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 250 8.3 strtok 和 sscanf 结合输入 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 256 8.4 解决爆栈,手动加栈 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 256 8.5 STL . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 256 8.5.1 优先队列 priority_queue . . . . . . . . . . . . . . . . . . . . . . . . . . . 256 8.5.2 set 和 multiset . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 257 8.6 输入输出外挂 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 258 8.7 莫队算法 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 258 8.7.1 分块 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 259 8.7.2 Manhattan MST 的 dfs 顺序求解 . . . . . . . . . . . . . . . . . . . . . . 260 8.8 VIM 配置 .
ACM Codebook(ACM代码手册)是一本整理了算法竞赛常用算法和数据结构的参考手册。它是为了帮助算法竞赛选手快速查阅各种算法和数据结构的实现而编写的。 ACM Codebook的内容非常丰富,包括了各种常用算法,如排序算法、图论算法、动态规划算法等。同时,它还包含了各种常用数据结构的实现,如链表、栈、队列、堆等。此外,ACM Codebook还介绍了一些常见的算法设计技巧和优化技巧,帮助选手更好地解决问题。 ACM Codebook的作用非常明显,首先它提供了各种算法和数据结构的实现代码,方便选手直接复制粘贴使用,节省了编写代码的时间。其次,ACM Codebook提供了详细的算法和数据结构的说明和示例,帮助选手理解和掌握这些算法和数据结构的原理和用法。最后,ACM Codebook还提供了一些常见问题的解决方案,帮助选手快速解决问题。 ACM Codebook的编写并不容易,需要作者具备扎实的算法和数据结构基础,并且对算法竞赛有深入的了解。编写ACM Codebook需要不断地修改和更新,以适应算法竞赛中不断变化的需求。 总之,ACM Codebook是一本非常有用的参考手册,它不仅提供了丰富的算法和数据结构的实现,还提供了对应的说明和示例,帮助算法竞赛选手快速掌握和应用这些算法和数据结构。它是算法竞赛选手在比赛中必备的工具之一。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

GGood_Name

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值