ACM总模板

1.基础

二分

int bsearch_1(int l, int r)
{
    while (l < r)
    {
        int mid = l + r >> 1;
        if (check(mid)) r = mid;
        else l = mid + 1;
    }
    return l;
}

int bsearch_2(int l, int r)
{
    while (l < r)
    {
        int mid = l + r + 1 >> 1;
        if (check(mid)) l = mid;
        else r = mid - 1;
    }
    return l;
}

快速幂

// a^b%c
ll qpow(ll a, ll b, ll c)
{
	ll res = 1;
	while(b) {
		if(b & 1) res = res * a % c;
		a = a * a % c;
		b >>= 1;
	}
	return res;
}

离散化

// a为原数组,b存储a的排序去重,c存储a的离散值
int a[maxn],b[maxn],c[maxn];
int n,m;
// 将a离散
void lisan(){
	for(int i=1;i<=n;i++) b[i]=a[i];
	sort(b+1,b+1+n);
	m=unique(b+1,b+1+n)-(b+1);
	for(int i=1;i<=n;i++) c[i]=lower_bound(b+1,b+1+m,a[i])-(b+1);
}

树上倍增查找

// fa[i][j]:i结点的第2^j级祖先
// 其中a[i]为结点i的权值,a数组递增
int fa[maxn][20];

for (int j=1;j<=18;j++)
        for (int i=1;i<=n;i++)
            fa[i][j]=fa[fa[i][j-1]][j-1];

// x:自给定的x结点起,查找链上权值小于等于r的最大结点            
for (int i=18;i>=0;i--) if (a[fa[x][i]]<=r) x=fa[x][i];

2.字符串

字符串hash

//自然溢出法

//统计前缀出现次数
#define base 131
#define ull unsigned long long
unordered_map<ull,int> mp;
string a;
void hash(char a[]){
	int len=strlen(a);
	ull t=0;
	for(int i=0;i<len;i++){
		t=t*base+a[i]-'a'+1;
		mp[t]++;
	}
}

//统计后缀出现次数
#define base 131
#define ull unsigned long long
unordered_map<ull,int> mp;
string a;
void hash(char a[]){
	int len=strlen(a);
	ull t=0,p=1;
	for(int i=len-1;i>=0;i--){
		t=t+p*(a[i]-'a'+1);
		p*=base;
		mp[t]++;
	}
}

KMP

// 最小循环节长度公式:minlen=len-ne[len-1]
//ne[i]:第i个前缀的最长公共前后缀长度 
int ne[maxn];
void getne(char* a){
	ne[0]=0;
	int len=strlen(a);
	for(int i=1,j=0;i<len;i++){
		while(a[j]!=a[i]&&j) j=ne[j-1];
		if(a[i]==a[j]) j++;
		ne[i]=j;
	}
}
//a:主串,b:模式串 
void find(char* a,char* b){
	getne(b);
	int lena=strlen(a),lenb=strlen(b);
	for(int i=0,j=0;i<lena;i++){
		while(b[j]!=a[i]&&j) j=ne[j-1];
		if(a[i]==b[j]) j++;
		if(j==lenb) printf("%lld\n",i-lenb+2);
	}
}

Trie(前缀树、字典树)

struct trie{
	int ch[maxn][30];	//ch[i][j]:结点i的第j个字符对应结点号 
	int val[maxn];
	int tot;
	int idx(char c){
		return c-'a';
	}
	void insert(char *s,int w){
		int u=0,n=strlen(s);
		for(int i=0;i<n;i++){
			int c=idx(s[i]);
			if(!ch[u][c]) ch[u][c]=++tot;
			u=ch[u][c];
		}
		val[u]+=w;
	}
	int query(char *s){
		int u=0,n=strlen(s);
		for(int i=0;i<n;i++){
			int c=idx(s[i]);
			u=ch[u][c];
			if(u==0) return 0;
		}
		return val[u];
	}
};

AC自动机(Trie+KMP)

将多个模式串压缩到Trie树中,并用KMP建立失配连接
得到AC自动机
再用主串去匹配AC自动机,可知所有模式串在主串中出现的信息

struct AC_automaton{
	int ch[maxn][30],val[maxn],tot;
	int ne[maxn];
	int idx(char c) {return c-'a';}
	void insert(char *s,int w){
		int u=0,n=strlen(s);
		for(int i=0;i<n;i++){
			int c=idx(s[i]);
			if(!ch[u][c]) ch[u][c]=++tot;
			u=ch[u][c];
		}
		val[u]+=w;
	}
	void getne(){
		queue<int> q;
		for(int i=0;i<26;i++) if(ch[0][i]) q.push(ch[0][i]);
		while(q.size()){
			int u=q.front();
			q.pop();
			for(int i=0;i<26;i++){
				if(ch[u][i]) ne[ch[u][i]]=ch[ne[u]][i], q.push(ch[u][i]);
				else ch[u][i]=ch[ne[u]][i];
			}
		}
	}
	int query(char *s){
		int u=0,n=strlen(s),ans=0;
		for(int i=0;i<n;i++){
			int c=idx(s[i]);
			u=ch[u][c];
			// 访问所有的失配点(同一个结点可能会在主串出现多次) 
			// 1.统计多少个模式串在主串s中出现过 
			for(int j=u;j&&val[j]!=-1;j=ne[j]) ans+=val[j],val[j]=-1;
			// 2.统计以结点j结尾的模式串在主串s中出现过多少次 
			// for(int j=u;j;j=ne[j]) if(val[j]) ans[j]++;
		}
		return ans;
	}
}AC;

后缀数组(待定)

将一个字符串进行压缩,高效处理所有子串信息

学习参考:Link

//未使用基数排序
//时间复杂度:O(nlognlogn)
#include <bits/stdc++.h>
using namespace std;
const int maxn=1e6+5;
char a[maxn];
//sa[i]:所有后缀在排完序后,排名为i的后缀在原串中的位置 
//rk[i]:所有后缀在排序完后,原字符串中第i名现在的排名 
//height[i]:suff[sa[i]]和suff[sa[i-1]]的最大公共前缀 
int len,sa[maxn],rk[maxn],height[maxn],oldrk[maxn],w;
int cmp(int a,int b)
{
	if(rk[a]==rk[b]) return rk[a+w]<rk[b+w];
	return rk[a]<rk[b];
}
int main()
{
	scanf("%s",a+1);
	len=strlen(a+1);
	//预处理 
	for(int i=1;i<=len;i++) sa[i]=i,rk[i]=a[i];
	//倍增 
	for(w=1;w<=len;w=(w<<1)){
		//排序(等于计算sa) 计算出当前排名的位 
		sort(sa+1,sa+1+len,cmp);
		//依据sa计算rk 		计算当前位的排名 
		memcpy(oldrk,rk,sizeof(rk));
		for(int i=1,cnt=0;i<=len;i++){
			//rk[sa[i]]=i;
			//若相邻排名转移而来大小相同则rk一致 
			if(oldrk[sa[i]]==oldrk[sa[i-1]]&&oldrk[sa[i]+w]==oldrk[sa[i-1]+w]){
				rk[sa[i]]=cnt;
			}
			else rk[sa[i]]=++cnt;	//不一致则排名增加 
		}
	}
	//求height
    int k=0;
    for(int i=1;i<=len;i++) rk[sa[i]]=i;
    for(int i=1;i<=len;i++){
        if(rk[i]==1) continue;
        int j=sa[rk[i]-1];
        k=(k>0?k-1:k);
        while(i+k<=len&&j+k<=len&&a[i+k]==a[j+k]) k++;
        height[rk[i]]=k;
    }
	for(int i=1;i<=len;i++) printf("%d ",sa[i]);
	return 0;
}
//使用基数排序
//时间复杂度:O(nlogn)
#include <bits/stdc++.h>
#define int long long
using namespace std;
const int maxn=1e6+5;
char a[maxn];
//sa[i]:所有后缀在排完序后,排名为i的后缀在原串中的位置 
//rk[i]:所有后缀在排序完后,原字符串中第i名现在的排名 
//height[i]:suff[sa[i]]和suff[sa[i-1]]的最大公共前缀 
int len,sa[maxn],rk[maxn],height[maxn],x[maxn],y[maxn],c[maxn];
int m=128;
signed main()
{
	scanf("%s",a+1);
	len=strlen(a+1);
	//求sa
	for(int i=1;i<=len;i++){
		x[i]=a[i];c[x[i]]++;
	}
	for(int i=2;i<=m;i++) c[i]+=c[i-1];
	for(int i=len;i>0;i--) sa[c[x[i]]--]=i;
	for(int w=1;w<=len;w<<=1){
		int cur=0;
		for(int i=len-w+1;i<=len;i++) y[++cur]=i;
		for(int i=1;i<=len;i++){
			if(sa[i]>w) y[++cur]=sa[i]-w;
		}
		for(int i=1;i<=m;i++) c[i]=0;
		for(int i=1;i<=len;i++) c[x[i]]++;
		for(int i=2;i<=m;i++) c[i]+=c[i-1];
		for(int i=len;i>0;i--){
			sa[c[x[y[i]]]--]=y[i];
			y[i]=0;
		}
		swap(x,y);
		x[sa[1]]=cur=1;
		for(int i=2;i<=len;i++){
			if(y[sa[i]]==y[sa[i-1]]&&y[sa[i]+w]==y[sa[i-1]+w]) x[sa[i]]=cur;
			else x[sa[i]]=++cur;
		}
		if(cur==len) break;
		m=cur;
	}
	//求rk
	for(int i=1;i<=len;i++) rk[sa[i]]=i;
	//求height
    int k=0;
    for(int i=1;i<=len;i++) rk[sa[i]]=i;
    for(int i=1;i<=len;i++){
        if(rk[i]==1) continue;
        int j=sa[rk[i]-1];
        k=(k>0?k-1:k);
        while(i+k<=len&&j+k<=len&&a[i+k]==a[j+k]) k++;
        height[rk[i]]=k;
    }
	for(int i=1;i<=len;i++) printf("%lld ",sa[i]);
	return 0;
}

manacher
求串中最长回文串的长度

//核心:求解len数组(le[i]:以i为回文中心的最长回文长度(直径))
char str[maxn];	//原字符串 
char tmp[maxn<<1];	//转换字符串 
int le[maxn<<1];	//回文长度数组 
int ans;
//转换字符串 
void init()
{
	int len=strlen(str),cnt=0;
	for(int i=0;i<len;i++){
		tmp[cnt++]='$';
		tmp[cnt++]=str[i];
	}
	tmp[cnt]='$';
}
void getle()
{
	int len=strlen(tmp),m=0,r=0;	//r:回文串最大右边界 m:对应的回文中心 
	//枚举i逐次求解le[i] 
	for(int i=0;i<len;i++){
		int j=2*m-i;		//j为i关于m的对称 
		if(i<r) le[i]=min(r-i,le[j]);	//i未越界直接解值 
		else le[i]=1;
		while(i-le[i]>=0&&tmp[i-le[i]]==tmp[i+le[i]]) le[i]++;	//回文枚举 
		//更新r和m 
		int r_new=i+le[i],m_new=i;
		if(r_new>r) r=r_new,m=m_new;
		ans=max(ans,le[i]-1);	//le[i]-1为原字符串回文长度 
	}
}

最小表示法

不断将字符串a[1~n]的最后一位放至开头,得到n个字符串,其中字典序最小的称为a的最小表示

char a[maxn];	// 从a[1]开始输入 
// 返回原串中最小表示的起点位置 
int sol(){
	int n=strlen(a+1);
	for(int i=1;i<=n;i++) a[n+i]=a[i];
	int i=1,j=2,k;
	while(i<=n&&j<=n){
		for(k=0;k<n&&a[i+k]==a[j+k];k++);
		if(k==n) break;
		if(a[i+k]>a[j+k]){
			i=i+k+1;
			if(i==j) i++;
		}
		else{
			j=j+k+1;
			if(i==j) j++;
		}
	}
	int ans=min(i,j);
	return ans;
}

最长上升子序列

bool operator > (string a,string b){
	if(a.compare(b)<=0) return false;
	return true;
}

//dp[i]:以第i个串为结尾的最长上升子序列长度
for(int i=1;i<cnt;i++){
	string now=a[i];
	if(!tot||now>tmp[tot]) tmp[++tot]=now,dp[i]=tot;
	else{
		int l=1,r=tot;
		//第一个比now大的 
		while(l<r){
			int m=(l+r)>>1;
			if(tmp[m]<=now) l=m+1;
			else r=m;
		}
		tmp[l]=now;
		dp[i]=l;
	}
}

求所有后缀的最长公共前缀

// lcp[i][j]:第i个后缀和第j个后缀的最长公共前缀长度
char a[maxn];
int lcp[5005][5005];
void getlcp(){
	lcp[len+1][len+1]=0;
	for (int i=len;i>=1;i--) {
        for (int j=len;j>=1;j--) {
            if (a[i]==a[j])
                lcp[i][j]=lcp[i+1][j+1]+1;
            else
                lcp[i][j]=0;
        }
    }
}

3.数据结构

并查集

int f[maxn];
int find(int a){
	if(f[a]==a||!f[a]) return a;
	return f[a]=find(f[a]);
}
void merge(int a,int b){
	f[find(a)]=find(b);
}

ST表
静态RMQ(只查询)
O(nlogn)预处理,O(1)回答

int n,a[maxn];	// a:原始序列 
struct ST{
	int f[maxn][30];	// f[i][j]:在区间[i,i+2^j-1]的最大值 
	void init(){
		for(int i=1;i<=n;i++) f[i][0]=a[i];
		int t=log(n)/log(2)+1;
		for(int j=1;j<t;j++)
			for(int i=1;i<=n-(1<<j)+1;i++)
				f[i][j]=max(f[i][j-1],f[i+(1<<(j-1))][j-1]);
	}
	// 查询区间[l,r]最大值 
	int query(int l,int r){
		int k=log(r-l+1)/log(2);
		return max(f[l][k],f[r-(1<<k)+1][k]);
	}
}st;

树状数组
点修改、区间查询
输入a数组后,调用build函数建立树状数组

int n,a[maxn];
struct Tree{
	int tree[maxn];	// 维护的是a数组
	int lowbit(int x){
		return x&-x;
	}
	// 将第x数加上val 
	void add(int x,int val){
		for(;x<=n;x+=lowbit(x)) tree[x]+=val;
	}
	void build(){
		for(int i=1;i<=n;i++) add(i,a[i]);
	}
	// 前缀和查询[1,x] 
	int ask(int x){
		int ans=0;
		for(;x;x-=lowbit(x)) ans+=tree[x];
		return ans;
	}
	// 查询区间[l,r] 
	int query(int l,int r){
		return ask(r)-ask(l-1);
	}
}tree;

树状数组
区间修改、单点查询
输入a数组后直接使用

int n,m,a[maxn];
struct Tree{
	int tree[maxn];	// 维护a的差分数组 
	int lowbit(int x){
		return x&-x;
	}
	// 将第x数加上val 
	void add(int x,int val){
		for(;x<=n;x+=lowbit(x)) tree[x]+=val;
	}
	// 差分前缀和 
	int ask(int x){
		int ans=0;
		for(;x;x-=lowbit(x)) ans+=tree[x];
		return ans;
	}
	// 区间修改:差分 
	void update(int l,int r,int val){
		add(l,val);
		add(r+1,-val);
	}
	// 单点查询 
	int query(int x){
		return a[x]+ask(x);
	}
}tree;

树状数组
区间修改(update)、区间查询(query)
输入a数组以后调用init函数初始化

int n,a[maxn];	// a:维护序列 
struct Tree{
	int c[2][maxn];	// 2颗树状数组 
	int sum[maxn];
	// 注意使用前调用初始化函数 
	void init(){
		for(int i=1;i<=n;i++) sum[i]=sum[i-1]+a[i];
	}
	int lowbit(int x){
		return x&-x;
	}
	int ask(int k,int x){
		int ans=0;
		for(;x;x-=lowbit(x)) ans+=c[k][x];
		return ans;
	}
	void add(int k,int x,int y){
		for(;x<=n;x+=lowbit(x)) c[k][x]+=y;
	}
	// 区间修改 
	void update(int l,int r,int val){
		add(0,l,val);
		add(0,r+1,-val);
		add(1,l,l*val);
		add(1,r+1,-(r+1)*val);
	}
	// 区间查询 
	int query(int l,int r){
		int ans=sum[r]+(r+1)*ask(0,r)-ask(1,r);
		ans-=sum[l-1]+l*ask(0,l-1)-ask(1,l-1);
		return ans;
	}
}tree;

线段树
区间修改([l,r]+val)、区间查询([l,r]的和)
使用build函数输入,整个区间为:[1,n]

struct Tree{
	int tree[maxn*4],lazy[maxn*4];
	void push_up(int u){
		int v1=u*2,v2=u*2+1;
		tree[u]=tree[v1]+tree[v2];
	}
	void push_down(int l,int r,int u){
		int v1=u*2,v2=u*2+1,m=(l+r)/2;
		tree[v1]+=lazy[u]*(m-l+1);
		tree[v2]+=lazy[u]*(r-m);
		lazy[v1]+=lazy[u];
		lazy[v2]+=lazy[u];
		lazy[u]=0;
	}
	// 建树:整个区间,根节点 
	void build(int l,int r,int u){
		if(l==r){
			scanf("%lld",&tree[u]);
			return;
		}
		int m=(l+r)/2;
		int v1=u*2,v2=u*2+1;
		build(l,m,v1);
		build(m+1,r,v2);
		push_up(u);
	}
	// 区间修改:整个区间,询问区间,权值,根节点 
	void update(int l,int r,int L,int R,int val,int u){
		if(l>=L&&r<=R){
			lazy[u]+=val;
			tree[u]+=(r-l+1)*val;
			return;
		}
		push_down(l,r,u);
		int m=(l+r)/2;
		int v1=u*2,v2=u*2+1;
		if(L<=m) update(l,m,L,R,val,v1);
		if(R>m) update(m+1,r,L,R,val,v2);
		push_up(u);
	}
	// 区间查询:整个区间,询问区间,根节点 
	int query(int l,int r,int L,int R,int u){
		if(l>=L&&r<=R) return tree[u];
		push_down(l,r,u);
		int m=(l+r)/2;
		int v1=u*2,v2=u*2+1;
		int tmp=0;
		if(L<=m) tmp+=query(l,m,L,R,v1);
		if(R>m) tmp+=query(m+1,r,L,R,v2);
		return tmp;
	}
}tree;

线段树
区间加修改,区间乘修改,区间和查询

int p;	// 模数 
struct Tree{
	// lazy:加法懒标记 lazy2:乘法懒标记 
	int tree[maxn*4],lazy[maxn*4],lazy2[maxn*4];
	void push_up(int u){
		int v1=u*2,v2=u*2+1;
		tree[u]=(tree[v1]+tree[v2])%p;
	}
	void push_down(int l,int r,int u){
		int v1=u*2,v2=u*2+1,m=(l+r)/2;
		tree[v1]=(tree[v1]*lazy2[u]+lazy[u]*(m-l+1))%p;
		tree[v2]=(tree[v2]*lazy2[u]+lazy[u]*(r-m))%p;
		lazy[v1]=(lazy[v1]*lazy2[u]+lazy[u])%p;
		lazy[v2]=(lazy[v2]*lazy2[u]+lazy[u])%p;
		lazy2[v1]=(lazy2[v1]*lazy2[u])%p;
		lazy2[v2]=(lazy2[v2]*lazy2[u])%p;
		lazy2[u]=1;
		lazy[u]=0;
	}
	// 建树:整个区间,根节点 
	void build(int l,int r,int u){
		lazy2[u]=1;
		lazy[u]=0;
		if(l==r){
			scanf("%lld",&tree[u]);
			tree[u]=tree[u]%p;
			return;
		}
		int m=(l+r)/2;
		int v1=u*2,v2=u*2+1;
		build(l,m,v1);
		build(m+1,r,v2);
		push_up(u);
	}
	// 加法区间修改:整个区间,询问区间,权值,根节点 
	void update(int l,int r,int L,int R,int val,int u){
		if(l>=L&&r<=R){
			lazy[u]=(lazy[u]+val)%p;
			tree[u]=(tree[u]+(r-l+1)*val)%p;
			return;
		}
		push_down(l,r,u);
		int m=(l+r)/2;
		int v1=u*2,v2=u*2+1;
		if(L<=m) update(l,m,L,R,val,v1);
		if(R>m) update(m+1,r,L,R,val,v2);
		push_up(u);
	}
	
	// 乘法区间修改 
	void update2(int l,int r,int L,int R,int val,int u){
		if(l>=L&&r<=R){
			lazy2[u]=(lazy2[u]*val)%p;
			lazy[u]=(lazy[u]*val)%p;
			tree[u]=(tree[u]*val)%p;
			return;
		}
		push_down(l,r,u);
		int m=(l+r)/2;
		int v1=u*2,v2=u*2+1;
		if(L<=m) update2(l,m,L,R,val,v1);
		if(R>m) update2(m+1,r,L,R,val,v2);
		push_up(u);
	}
	
	// 区间和查询:整个区间,询问区间,根节点 
	int query(int l,int r,int L,int R,int u){
		if(l>=L&&r<=R) return tree[u];
		push_down(l,r,u);
		int m=(l+r)/2;
		int v1=u*2,v2=u*2+1;
		int tmp=0;
		if(L<=m) tmp=(tmp+query(l,m,L,R,v1))%p;
		if(R>m) tmp=(tmp+query(m+1,r,L,R,v2))%p;
		return tmp;
	}
}tree;

线段树
动态RMQ(修改+查询)

struct Tree{
	int tree[maxn*4],lazy[maxn*4];
	void push_up(int u){
		int v1=u*2,v2=u*2+1;
		tree[u]=max(tree[v1],tree[v2]);
	}
	void push_down(int u){
		int v1=u*2,v2=u*2+1;
		tree[v1]+=lazy[u];
		tree[v2]+=lazy[u];
		lazy[v1]+=lazy[u];
		lazy[v2]+=lazy[u];
		lazy[u]=0;
	}
	// 建树:整个区间,根节点 
	void build(int l,int r,int u){
		if(l==r){
			scanf("%lld",&tree[u]);
			return;
		}
		int m=(l+r)/2;
		int v1=u*2,v2=u*2+1;
		build(l,m,v1);
		build(m+1,r,v2);
		push_up(u);
	}
	// 区间修改:整个区间,询问区间,权值,根节点 
	void update(int l,int r,int L,int R,int val,int u){
		if(l>=L&&r<=R){
			lazy[u]+=val;
			tree[u]+=val;
			return;
		}
		push_down(u);
		int m=(l+r)/2;
		int v1=u*2,v2=u*2+1;
		if(L<=m) update(l,m,L,R,val,v1);
		if(R>m) update(m+1,r,L,R,val,v2);
		push_up(u);
	}
	// 区间查询:整个区间,询问区间,根节点 
	int query(int l,int r,int L,int R,int u){
		if(l>=L&&r<=R) return tree[u];
		push_down(u);
		int m=(l+r)/2;
		int v1=u*2,v2=u*2+1;
		int tmp=0;
		if(L<=m) tmp=max(tmp,query(l,m,L,R,v1));
		if(R>m) tmp=max(tmp,query(m+1,r,L,R,v2));
		return tmp;
	}
}tree;

静态主席树(可持久化权值线段树)
查询数组区间第k大
输入a数组以后,使用build创建主席树,使用query查询区间第k大
如需实现不同功能,修改其query函数即可

// 支持a范围:[-1e9,1e9],需离散
// n:a(原)数组长度,m:b数组长度 
int n,m,a[maxn],b[maxn];
struct Tree{
	// T[i]:原数组下标为i的数的根结点 
	// val[i]:主席树结点为i的权值 
	// L[i]:主席树结点为i的左儿子,R类似 
	int T[maxn],val[maxn*40],L[maxn*40],R[maxn*40];	// 主席树开40倍空间 
	int tot;
	// 上颗树节点,区间,x:插入的值a[i] 
	int insert(int pre,int l,int r,int x){
		int rt=++tot;
		int m=(l+r)/2;
		L[rt]=L[pre],R[rt]=R[pre],val[rt]=val[pre]+1;
		if(l<r){
			if(x<=m) L[rt]=insert(L[pre],l,m,x);
			else R[rt]=insert(R[pre],m+1,r,x);
		}
		return rt;
	}
	// 据a数组创建主席树 
	void build(){
		// 将a离散:a变原数组对应离散值,b变原数组的排序去重 
		for(int i=1;i<=n;i++) b[i]=a[i];
		sort(b+1,b+1+n);
		m=unique(b+1,b+1+n)-(b+1);
		for(int i=1;i<=n;i++){
			a[i]=lower_bound(b+1,b+1+m,a[i])-b;
			T[i]=insert(T[i-1],1,m,a[i]);
		}
	}
	// 返回区间第k大的下标值 
	// 查询区间对应的根节点,总区间,第k大 
	int query(int u,int v,int l,int r,int k){
		if(l==r) return l;
		int x=val[L[v]]-val[L[u]];
		int m=(l+r)/2;
		if(x>=k) return query(L[u],L[v],l,m,k);
		else return query(R[u],R[v],m+1,r,k-x);
	}
}tree;

/*
查询区间第k大的值
scanf("%lld%lld%lld",&l,&r,&k);
int pos=tree.query(tree.T[l-1],tree.T[r],1,m,k);
printf("%lld\n",b[pos]);
*/

静态主席树
a为结点值。对树使用主席树,按dfs序建主席树
题目参考

// 支持a范围:[1,1e9],无需离散
int n,a[maxn];
struct Tree{
	// T[i]:原数组下标为i的数的根结点 
	// val[i]:主席树结点为i的权值 
	// L[i]:主席树结点为i的左儿子,R类似 
	int T[maxn],val[maxn*40],L[maxn*40],R[maxn*40];	// 主席树开40倍空间 
	int tot;
	// 上颗树节点,区间,x:插入的值a[i] 
	int insert(int pre,int l,int r,int x){
		int rt=++tot;
		int m=(l+r)/2;
		L[rt]=L[pre],R[rt]=R[pre],val[rt]=val[pre]+1;
		if(l<r){
			if(x<=m) L[rt]=insert(L[pre],l,m,x);
			else R[rt]=insert(R[pre],m+1,r,x);
		}
		return rt;
	}
	// 查询左边界树的根,查询右边界的根,总区间,查询大于等于k的数量 
	int ask(int u,int v,int l,int r,int k){
		int x=val[v]-val[u];
		if(l>=k) return x;
		if(r<k) return 0;
		int m=(l+r)/2;
		return ask(L[u],L[v],l,m,k)+ask(R[u],R[v],m+1,r,k);
	}
}tree;
// 求解dfs序(dfn1,dfn2),并按dfs序建树
void dfs(int u,int fa){
	dfn1[u]=++all;
	tree.T[all]=tree.insert(tree.T[all-1],1,N,a[u]);
	for(int i=head[u];i;i=edge[i].ne){
		int v=edge[i].to;
		if(v==fa) continue;
		dfs(v,u);
	}
	dfn2[u]=all;
}
// tree.ask(tree.T[dfn1[x]-1],tree.T[dfn2[x]],1,N,l)

求树的重心

// 树的重心为f[i]最小的点
// 树的重心:最大子树的结点数最小 
// f[i]:以i为根的最大子树的结点数 
// size[i]:以i为根的子树的结点数 
int f[maxn],size[maxn],all;
vector<int> ve[maxn];
void dfs(int u,int fa){
	size[u]=1;
	for(int v:ve[u]){
		if(v==fa) continue;
		dfs(v,u);
		size[u]+=size[v];
		f[u]=max(f[u],size[v]);
	}
	f[u]=max(f[u],all-size[u]);
}

最近公共祖先LCA(倍增)

int n,m,s;
// fa[i][j]:i结点的第2^j级祖先 
// dep[i]:结点i的深度 
int fa[maxn][25],dep[maxn];
vector<int> ve[maxn];
// 求dep[i],并初始化f[u][0] 
void dfs(int u,int fa,int d){
	dep[u]=d;
	::fa[u][0]=fa;
	for(int v:ve[u]){
		if(!dep[v]) dfs(v,u,d+1);
	}
}
int LCA(int x,int y){
	if(dep[x]<dep[y]) swap(x,y);	// 默认x更深 
	for(int i=20;i>=0;i--)
		if(dep[y]<=dep[x]-(1<<i)) x=fa[x][i];
    if(x==y) return x;
    for(int i=20;i>=0;i--){
    	if(fa[x][i]!=fa[y][i]) x=fa[x][i],y=fa[y][i];
    }
    return fa[x][0];
}
signed main()
{
	cin>>n>>m>>s;
	for(int i=1;i<=n-1;i++){
		int u,v;
		scanf("%lld%lld",&u,&v);
		ve[u].push_back(v);
		ve[v].push_back(u);
	}
	// dfs初始化后求所有的fa[i][j] 
	dfs(s,0,1);
	for(int j=1;j<=20;j++)
        for(int i=1;i<=n;i++)
            fa[i][j]=fa[fa[i][j-1]][j-1];
	while(m--){
		int u,v;
		scanf("%lld%lld",&u,&v);
		printf("%lld\n",LCA(u,v));
	}
	return 0;
}

点分治

const int maxn = 1e6+5;
vector<int> edge[maxn];
int rt,size[maxn],son[maxn];
int all;
int vis[maxn];
int ans;
void getroot(int u, int fa)
{
	size[u] = 1;
	son[u] = 0;
	int tmp = 0;
	for(auto v : edge[u]){
		if(v == fa) continue;
		getroot(v, u);
		size[u] += size[v];
		son[u] = max(son[u], size[v]);	//son[u]:u结点最大的儿子块 
	}
	son[u] = max(son[u], all - size[u]);
	if(son[u] < son[rt]) rt = u;	//最大儿子最小的为重心 
}
void dfs(int u)
{
	vis[u] = 1;
	ans += cal(u);
	for(auto v : edge[u]){
		if(vis[v]) continue;
		ans -= cal(v);
		rt = 0;	//son[0]为inf,保证第一次rt能被置换 
		all = size[v];
		getroot(v, 0);
		dfs(rt);
	}
}

树上启发式合并

dsu on tree
1.轻重链剖分,找出重儿子。(预处理)
2.先处理轻儿子,不记录贡献。
3.处理重儿子,记录贡献。
4.将当前节点和轻儿子的贡献和重儿子合并。
5.如果当前节点是轻儿子的话,消除影响。
模板题(给出一个树,求出每个节点的子树中出现次数最多的颜色的编号和)
参考博客

在这里插入图片描述

// 结点的大小,重儿子结点号,结点颜色,颜色数,最大颜色数,重儿子 
int size[maxn],son[maxn],col[maxn],cnt[maxn],Mx,Son;
int sum,ans[maxn];
vector<int> ve[maxn];
// 预处理求son[u] 
void dfs(int u,int fa){
	size[u]=1;
	for(int v:ve[u]){
		if(v==fa) continue;
		dfs(v,u);
		size[u]+=size[v];
		if(size[v]>size[son[u]]) son[u]=v;
	}
}
void add(int u,int fa,int val){
	cnt[col[u]]+=val;//这里可能会因题目而异
    if(cnt[col[u]]>Mx) Mx=cnt[col[u]],sum=col[u];
    else if(cnt[col[u]]==Mx) sum+=col[u];
    for(int v:ve[u]){
    	if(v==fa||v==Son) continue;
    	add(v,u,val);
    }
}
// 求ans[u] 
void dfs2(int u,int fa,int op){
	for(int v:ve[u]){
		if(v==fa) continue;
		if(v!=son[u]) dfs2(v,u,0);//暴力统计轻边的贡献,opt = 0表示递归完成后消除对该点的影响
	}
	if(son[u]) dfs2(son[u],u,1),Son=son[u];//统计重儿子的贡献,不消除影响
	add(u,fa,1); Son=0;//暴力统计所有轻儿子的贡献
	ans[u]=sum;//更新答案
	if(!op) add(u,fa,-1),sum=0,Mx=0;//如果需要删除贡献的话就删掉
}
int n;
signed main()
{
	cin>>n;
	for(int i=1;i<=n;i++) scanf("%lld",&col[i]);
	for(int i=1;i<=n-1;i++){
		int u,v;
		scanf("%lld%lld",&u,&v);
		ve[u].push_back(v);
		ve[v].push_back(u);
	}
	dfs(1,0);
    dfs2(1,0,0);
    for(int i=1;i<=n;i++) printf("%lld ",ans[i]);
	return 0;
}

4.数学

求组合数

double c[105][105];
void init(){
	for(int i=0;i<=100;i++){
		for(int j=0;j<=i;j++){
			if(j==0) c[i][j]=1;
			else c[i][j]=c[i-1][j]+c[i-1][j-1];
		}
	}
}

可重复排列组合
请添加图片描述
多重集全排列个数
题目参考
在这里插入图片描述

辗转相除法

gcd(a,b)=gcd(a%b,b)
当题目给出a%b=1时注意考虑(互质)
差分与gcd
g c d ( a , b , c , d . . . ) = g c d ( a , b − a , c − b , d − c ) gcd(a,b,c,d...)=gcd(a,b-a,c-b,d-c) gcd(a,b,c,d...)=gcd(a,ba,cb,dc)

二项式定理
在这里插入图片描述

二项分布
在这里插入图片描述

几何分布

n n n 次伯努利实验中,第 k k k 次实验才得到第一次成功的概率分布,令该事件为 A A A,可以理解为前 k − 1 k-1 k1 次皆失败,第 k k k 次成功的概率。
P ( k ) = ( 1 − p ) k − 1 ∗ p P ( k ) = ( 1 − p )^{ k − 1} ∗ p P(k)=(1p)k1p
E = 1 P ( k ) E=\frac1{P(k)} E=P(k)1 A A A发生一次期望的实验次数)

期望

期望= ∑ \sum\limits_{} 概率x权值

容斥原理

容斥原理
奇加偶减
A ∪ B ∪ C = A + B + C − A ∩ B − A ∩ C − B ∩ C + A ∩ B ∩ C A∪B∪C=A+B+C-A∩B-A∩C-B∩C+A∩B∩C ABC=A+B+CABACBC+ABC

逆元

即求关于x的同余方程 ax ≡ 1 (mod m) 的最小正整数解
即解方程a*inv + my = 1(已知a和m,求一组特殊解使得inv最小)

// 函数返回值是a和b的最大公约数 
int exgcd(int a,int b,int &x,int &y){
	if(b==0){
		x=1;
		y=0;
		return a;
	}
	int ans=exgcd(b,a%b,x,y);
	int tmp=x;
	x=y;
	y=tmp-a/b*y;
	return ans;
}
int inv(int a,int m){
	int x,y,gcd=exgcd(a,m,x,y);
	if(gcd!=1) return -1;	// 不存在逆元(需a和m互质) 
	return (x%m+m)%m;
}

矩阵快速幂

struct mat {
    int r,c;
    int m[105][105];        //经测试最大开成590*590的 ll 型矩阵
    mat() {}
    mat(int r,int c):r(r),c(c) {}
    void clear() {
        memset(m,0,sizeof(m));
    }

    mat operator+(mat a)const {
        mat ans(r,c);
        for(int i=1; i<=r; i++) {
            for(int j=1; j<=c; j++) {
                ans.m[i][j]=(m[i][j]+a.m[i][j])%mod;
            }
        }
        return ans;
    }

    mat operator*(mat a)const {
        mat tmp(r,a.c);
        int i,j,k;
        for(i=1; i<=tmp.r; i++) {
            for(j=1; j<=tmp.c; j++) {
                tmp.m[i][j]=0;
                for(k=1; k<=c; k++) {
                    tmp.m[i][j]=(tmp.m[i][j]+(m[i][k]*a.m[k][j])%mod)%mod;
                }
            }
        }
        return tmp;
    }

	// 算矩阵幂:a^n
    mat operator^(int n)const {       //需要时可以用 ll n,注意运算符优先级比较低,多用括号;
        mat ans(r,r),tmp(r,r);
        memcpy(tmp.m,m,sizeof(tmp.m));
        ans.clear();
        for(int i=1; i<=ans.r; i++) {
            ans.m[i][i]=1;
        }
        while(n) {
            if(n&1)ans=ans*tmp;
            n>>=1;
            tmp=tmp*tmp;
        }
        return ans;
    }

    void print()const {
        for(int i=1; i<=r; i++) {
            for(int j=1; j<=c; j++) {
                printf("%lld",m[i][j]);
                if(j==c)printf("\n");
                else printf(" ");
            }
        }
    }

};

高斯消元

// a为方程组 
int n;
double a[105][105];
// 高斯-约旦消元法
void Gauss(){
	for(int i=1;i<=n;i++){
		int now=i;
		for(int j=i+1;j<=n;j++){
			if(fabs(a[j][i])>fabs(a[now][i])) now=j;
		}
		for(int j=1;j<=n+1;j++) swap(a[i][j],a[now][j]);
		if(!a[i][i]){
			puts("No Solution");
			return;
		}
		for(int j=1;j<=n;j++){
			if(j!=i){
				double tmp=a[j][i]/a[i][i];
				for(int k=i+1;k<=n+1;k++) a[j][k]-=a[i][k]*tmp;
			}
		}
	}
	// 上述操作结束后,矩阵会变成这样
    /*
    a1*x1=b1
    a2*x2=b2
    a3*x3=b3
    a4*x4=b4
    */
    // 用b/a消除系数 
    for(int i=1;i<=n;i++)
    printf("%.2lf\n",a[i][n+1]/a[i][i]);
}

5.图论

链式前向星

int head[maxn],tot;
struct edge{
	int to,val,ne;
}edge[maxn];
void add_edge(int now,int to,int val){
	tot++;
	edge[tot].ne=head[now];
	head[now]=tot;
	edge[tot].to=to;
	edge[tot].val=val;
}
void dfs(int u,int fa){
	for(int i=head[u];i;i=edge[i].ne){
		int v=edge[i].to;
		if(v==fa) continue;
		dfs(v,u);
	}
}

vector建图

// 适用于无边权
vector<int> ve[maxn];
void add_edge(int u,int v){
	ve[u].push_back(v);
}
void dfs(int u,int fa){
	for(int v:ve[u]){
		if(v==fa) continue;
		dfs(v,u);
	}
}

堆优化dijkstra

单源最短路径,求一个点到其他所有点的最短路径

// 点数,边数,源点到各点最短距离,起点,访问标记,链式head 
int n,m,dist[maxn],s,vis[maxn],head[maxn];
struct edge{
	int to,val,ne;
}edge[maxn];
struct node{
	int val,pos;
	friend bool operator <(struct node a,struct node b){
		return a.val>b.val;
	}
};
priority_queue<node> q;	// 堆优化 
void add_edge(int now,int to,int val,int tot){
	edge[tot].ne=head[now];
	head[now]=tot;
	edge[tot].to=to;
	edge[tot].val=val;
}
void dij(){
	for(int i=1;i<=n;i++) if(i!=s) dist[i]=inf;
	q.push({0,s});
	while(!q.empty()){
		struct node top=q.top();
		q.pop();
		if(vis[top.pos]) continue;
		vis[top.pos]=1;
		for(int i=head[top.pos];i;i=edge[i].ne){
			int to_val=top.val+edge[i].val;
			int to_pos=edge[i].to;
			if(dist[to_pos]>to_val){
				dist[to_pos]=to_val;
				q.push({to_val,to_pos});
			}
		}
	}
}

Floyd(n^3)

求每两点之间的最短路径(不必调用n次dijkstra)

void floyd(){
	// 初始化 
	for(int i=1;i<=n;i++){
		for(int j=1;j<=n;j++){
			if(i!=j) f[i][j]=inf;
		}
	}
	for(int k=1;k<=n;k++){
		for(int i=1;i<=n;i++){
			for(int j=1;j<=n;j++){
				f[i][j]=min(f[i][j],f[i][k]+f[k][j]);
			}
		}
	}
}

6.DP

背包

// 01背包for (int i=1;i<=n;i++)
    for (int j=V;j>=w[i];j--)
        f[j]=max(f[j],f[j-w[i]]+v[i]);

// 完全背包
for (int i=1;i<=n;i++)
    for (int j=w[i];j<=V;j++)
        f[j]=max(f[j],f[j-w[i]]+v[i]);


悬线法
在这里插入图片描述

数位dp
在这里插入图片描述

int a,b,dig[15],pos;
int dp[15][15][2][2];
//dp[pos][cnt]:前pos位数字x出现了cnt次对应的答案 
int dfs(int pos,int cnt,int zero,int lim,int now){
	if(!pos) return cnt;
	if(dp[pos][cnt][zero][lim]!=-1) return dp[pos][cnt][zero][lim];
	int up=(lim?dig[pos]:9);
	int tmp=0;
	for(int i=0;i<=up;i++){
		if(zero&&i==0) tmp+=dfs(pos-1,cnt,zero&&i==0,lim&&i==up,now);
		else tmp+=dfs(pos-1,cnt+(i==now),zero&&i==0,lim&&i==up,now);
	}
	return dp[pos][cnt][zero][lim]=tmp;
}
int sol(int x,int i){
	memset(dp,-1,sizeof(dp));
	pos=0;
	while(x){
		dig[++pos]=x%10;
		x/=10;
	}
	return dfs(pos,0,1,1,i);
}
signed main()
{
	scanf("%lld%lld",&a,&b);
	for(int i=0;i<=9;i++) printf("%lld ",sol(b,i)-sol(a-1,i));
	return 0;
}

在这里插入图片描述

int t,x,y,a[maxn],b[maxn],pos1,pos2,dp[maxn][2][2],ans;
int dfs(int pos,int lim1,int lim2,int ok){
	if(pos==0) return 1;
	if(dp[pos][lim1][lim2]!=-1) return dp[pos][lim1][lim2];
	int up1=(lim1?a[pos]:1),up2=(lim2?b[pos]:1);
	int res=0,val=0;
	for(int i=0;i<=up1;i++){
		for(int j=0;j<=up2;j++){
			if(i==1&&j==1) continue;
			int tmp=dfs(pos-1,lim1&&i==up1,lim2&&j==up2,ok||i||j);
			res=(res+tmp)%mod;
			if(!ok&&(i||j)) val=(val+tmp)%mod;
		}
	}
	ans=(ans+pos*val)%mod;
	return dp[pos][lim1][lim2]=res;
}
void sol(int x,int y){
	while(x){
		a[++pos1]=x%2;
		x/=2;
	}
	while(y){
		b[++pos2]=y%2;
		y/=2;
	}
	dfs(max(pos1,pos2),1,1,0);
}
signed main()
{
	cin>>t;
	while(t--){
		ans=pos1=pos2=0;
		memset(a,0,sizeof(a));
		memset(b,0,sizeof(b));
		memset(dp,-1,sizeof(dp));
		scanf("%lld%lld",&x,&y);
		sol(x,y);
		printf("%lld\n",ans);
	}
	return 0;
}

概率dp
在这里插入图片描述

int t,n,m,k;
double c[maxn][maxn],p[maxn],dp[maxn][maxn],ans;
void init(){
	p[0]=1;
	for(int i=1;i<=100;i++) p[i]=p[i-1]*0.5;
	c[0][0]=1;
	for(int i=1;i<=100;i++){
		for(int j=0;j<=100;j++){
			c[i][j]=c[i-1][j]+c[i-1][j-1];
		}
	}
}
signed main()
{
	init();
	cin>>t;
	while(t--){
		ans=0;
		memset(dp,0,sizeof(dp));
		dp[0][0]=1;
		scanf("%lld%lld%lld",&n,&m,&k);
		for(int i=0;i<=m;i++){
			for(int j=0;j<=n;j++){
				for(int x=0;x<=k;x++){
					if(n-j>=k)
					dp[i+1][j+x]+=dp[i][j]*p[k]*c[k][x];
					else
					dp[i+1][j+x-(k-(n-j))]+=dp[i][j]*p[k]*c[k][x];
				}
			}
		}
		for(int i=0;i<=n;i++) ans+=dp[m][i]*i;
		printf("%.3lf\n",ans);
	}
	return  0;
}

7.计算几何

const double eps=1e-8;
int sgn(double x){
	if(fabs(x)<eps) return 0;
	if(x>0) return 1;
	return -1;
}

// 点类 
struct point{
	double x,y;
	point() {}
	point(double x,double y): x(x),y(y) {}
	point operator - (point b){
		return point{x-b.x,y-b.y};
	}
	// 点积 
	double operator * (point b){
		return x*b.x+y*b.y;
	}
	// 叉积 
	double operator ^ (point b){
		return x*b.y-y*b.x;
	}
	// 绕原点旋转A弧度(注意A是弧度,非角度) 
	void rotate(double A){
		double tx=x,ty=y;
		x=tx*cos(A)-ty*sin(A);
        y=tx*sin(A)+ty*cos(A);
	}
};

// 线类 
struct line{
	point s,e;
	line() {}
	line(point s,point e): s(s),e(e) {}
	//两直线求交点
    pair<int,point> operator & (line b){
        point ans=s;
        if (sgn((s - e) ^ (b.s - b.e)) == 0){
        	if (sgn((s - b.e) ^ (b.s - b.e)) == 0)
                return make_pair(0,ans); //重合
            else
                return make_pair(1,ans); //平行
        }
        double t = ((s - b.s) ^ (b.s - b.e)) / ((s - e) ^ (b.s - b.e));
        ans.x += (e.x - s.x) * t;
        ans.y += (e.y - s.y) * t;
        return make_pair(2, ans);
    }
};
//求两点间距离 
double dist(point a,point b){
	return sqrt((a-b)*(a-b));
}
//判点在线段上 
bool PointOnSegment(point p,line l){
	return sgn( (p-l.s) ^ (l.e-l.s) )==0 && sgn( (p-l.s) * (p-l.e) )<=0;
}
//判线段相交
bool inter(line l1, line l2){
	return max(l1.s.x, l1.e.x) >= min(l2.s.x, l2.e.x) &&
           max(l2.s.x, l2.e.x) >= min(l1.s.x, l1.e.x) &&
           max(l1.s.y, l1.e.y) >= min(l2.s.y, l2.e.y) &&
           max(l2.s.y, l2.e.y) >= min(l1.s.y, l1.e.y) &&
           sgn((l2.s - l1.e) ^ (l1.s - l1.e)) * sgn((l2.e - l1.e) ^ (l1.s - l1.e)) <= 0 &&
           sgn((l1.s - l2.e) ^ (l2.s - l2.e)) * sgn((l1.e - l2.e) ^ (l2.s - l2.e)) <= 0;
}
//判直线与线段相交,l2为线段
bool seg_inter_line(line l1, line l2){
	return sgn((l2.s - l1.e) ^ (l1.s - l1.e)) * sgn((l2.e - l1.e) ^ (l1.s - l1.e)) <= 0;
}
//点到直线的距离,返回点到直线最近的点
point PointToLine(point P,line L){
	point ans;
    double t = ((P - L.s) * (L.e - L.s)) / ((L.e - L.s) * (L.e - L.s));
    ans.x = L.s.x + (L.e.x - L.s.x) * t;
    ans.y = L.s.y + (L.e.y - L.s.y) * t;
    return ans;
}
//点到线段的距离
point NearestPointToLineSeg(point P, line L){
	point ans;
    double t = ((P - L.s) * (L.e - L.s)) / ((L.e - L.s) * (L.e - L.s));
    if (t >= 0 && t <= 1){
    	ans.x = L.s.x + (L.e.x - L.s.x) * t;
        ans.y = L.s.y + (L.e.y - L.s.y) * t;
    }
    else{
    	if (dist(P, L.s) < dist(P, L.e))
            ans = L.s;
        else
            ans = L.e;
    }
    return ans;
}

// 多边形类 
struct polygon{
	int n;
	point a[maxn];
	polygon() {}
	// 计算面积 
	double area(){
		double ans=0;
		a[n]=a[0];
		for(int i=0;i<n;i++) ans+=(a[i+1]^a[i]);
		return ans/2;
	}
	// 判断点是否在多边形内部(0外,1内,2边界) 
	int point_in(point p){
		int num=0;
		a[n]=a[0];
		for(int i=0;i<n;i++){
			if(PointOnSegment(p,{a[i],a[i+1]})) return 2;
			int k=sgn( (a[i+1]-a[i]) ^ (p-a[i]) );
			int d1=sgn(a[i].y-p.y);
			int d2=sgn(a[i+1].y-p.y);
			if(k>0&&d1<=0&&d2>0) num++;
			if(k<0&&d2<=0&&d1>0) num--;
		}
		return num!=0;
	}
};

其他

关闭cin cout同步

#define IOS ios::sync_with_stdio(false); cin.tie(0); cout.tie(0)

输入输出重定向

freopen("D:\\in.txt","r",stdin);
freopen("D:\\out.txt","w",stdout);

一行读入

int a[maxn],n;
while(~scanf("%lld",&a[n++]));
n--;

string a;
getline(cin,a);

char a[maxn];
scanf("%[^\n]",a);

string 转换(string转数字,数字转string)

在这里插入图片描述
在这里插入图片描述

全局long long

#include <bits/stdc++.h>
#define int long long
using namespace std;
const int maxn=1e5+5;
const int mod=1e9+7;
const int inf=1e18;
signed main()
{
	return 0;
}

程序计时

cin>>n;		/*用户初始输入部分*/
double t1=clock();	//一定要置于初始输入后面,忽略输入时间
/*
代码主体部分
*/
double t2=clock();
cout<<"\nTime: "<<t2-t1<<"ms"<<endl;

技巧

有环图DP(无论有向图或无向图)

在图进行DP时,因为有环的情况
需要保证状态只能从父结点向子结点转移,而不能由子结点向父结点转移
由于要保证父结点向子结点转移,故需设计好状态,将问题进行一定的转化,使用DFS和BFS均有可能
题目参考
题目参考

取模输出答案

若题目要求取模输出答案,算完后需执行以下操作
因为计算时可能有减操作,由于取模的性质算出负数

ans=(ans+mod)%mod;

计算空间复杂度

字节数 / 1024 = xx KB
例子:
题目空间限制为262144K
定义数组int a[6e7],则占用空间6e7*4 /1024 = 234375 K,未炸空间

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 配置 .
1 图论 3 1.1 术语 3 1.2 独立集、覆盖集、支配集之间关系 3 1.3 DFS 4 1.3.1 割顶 6 1.3.2 桥 7 1.3.3 强连通分量 7 1.4 最小点基 7 1.5 拓扑排序 7 1.6 欧拉路 8 1.7 哈密顿路(正确?) 9 1.8 Bellman-ford 9 1.9 差分约束系统(用bellman-ford解) 10 1.10 dag最短路径 10 1.11 二分图匹配 11 1.11.1 匈牙利算法 11 1.11.2 KM算法 12 1.12 网络流 15 1.12.1 最大流 15 1.12.2 上下界的网络的最大流 17 1.12.3 上下界的网络的最小流 17 1.12.4 最小费用最大流 18 1.12.5 上下界的网络的最小费用最小流 21 2 数论 21 2.1 最大公约数gcd 21 2.2 最小公倍数lcm 22 2.3 快速幂取模B^LmodP(O(logb)) 22 2.4 Fermat小定理 22 2.5 Rabin-Miller伪素数测试 22 2.6 Pollard-rho 22 2.7 扩展欧几里德算法extended-gcd 24 2.8 欧拉定理 24 2.9 线性同余方程ax≡b(mod n) 24 2.10 中国剩余定理 25 2.11 Discrete Logging(BL == N (mod P)) 26 2.12 N!最后一个不为0的数字 27 2.13 2^14以内的素数 27 3 数据结构 31 3.1 堆(最小堆) 31 3.1.1 删除最小值元素: 31 3.1.2 插入元素和向上调整: 32 3.1.3 堆的建立 32 3.2 并查集 32 3.3 树状数组 33 3.3.1 LOWBIT 33 3.3.2 修改a[p] 33 3.3.3 前缀和A[1]+…+A[p] 34 3.3.4 一个二维树状数组的程序 34 3.4 线段树 35 3.5 字符串 38 3.5.1 字符串哈希 38 3.5.2 KMP算法 40 4 计算几何 41 4.1 直线交点 41 4.2 判断线段相交 41 4.3 三点外接圆圆心 42 4.4 判断点在多边形内 43 4.5 两圆交面积 43 4.6 最小包围圆 44 4.7 经纬度坐标 46 4.8 凸包 46 5 Problem 48 5.1 RMQ-LCA 48 5.1.1 Range Minimum Query(RMQ) 49 5.1.2 Lowest Common Ancestor (LCA) 53 5.1.3 Reduction from LCA to RMQ 56 5.1.4 From RMQ to LCA 57 5.1.5 An algorithm for the restricted RMQ 60 5.1.6 An AC programme 61 5.2 最长公共子序列LCS 64 5.3 最长上升子序列/最长不下降子序列(LIS) 65 5.3.1 O(n^2) 65 5.3.2 O(nlogn) 66 5.4 Joseph问题 67 5.5 0/1背包问题 68 6 组合数学相关 69 6.1 The Number of the Same BST 69 6.2 排列生成 71 6.3 逆序 72 6.3.1 归并排序求逆序 72 7 数值分析 72 7.1 二分法 72 7.2 迭代法(x=f(x)) 73 7.3 牛顿迭代 74 7.4 数值积分 74 7.5 高斯消元 75 8 其它 77
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值