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,未炸空间

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值