Nickle的基础模板库!

前言

目录在右下角可打开。文章可能过长,建议使用目录浏览。


基本算法


前缀和

一维前缀和

二维前缀和

差分

一维差分

二维差分


排序

逆序对

CDQ分治

#include<bits/stdc++.h>
using namespace std;
const int N=1e6+105;
int a[N],b[N],n;
long long ans;
void CDQ(int l,int r,int *a){
	if(l==r) return ;
	int mid=(l+r)>>1;
	CDQ(l,mid,a);CDQ(mid+1,r,a);
	int t1=l,t2=mid+1;
	for(int i=l;i<=r;i++)
		if(a[t1]>a[t2] && t2<=r || t1>mid) b[i]=a[t2++],ans+=mid-t1+1;
		else b[i]=a[t1++];
	for(int i=l;i<=r;i++)a[i]=b[i];
}
namespace Read{
    template<typename T>
    inline void read(T &x){
        x=0;T f=1;char ch=getchar();
        while (!isdigit(ch)) {if(ch=='-')f=-1;ch=getchar();}
        while (isdigit(ch)){x=x*10+(ch^48);ch=getchar();}
        x*=f;
    }

    template <typename T, typename... Args>
    inline void read(T& t, Args&... args) {
        read(t); read(args...);
    }
}
using namespace Read;
int main(){
	read(n);
	for(int i=1;i<=n;i++)read(a[i]);
	CDQ(1,n,a);
	printf("%lld",ans);
	return 0;
}

归并排序

#include<bits/stdc++.h>
#define ll unsigned long long
using namespace std;
int n,a[500005][2];
ll ans;
inline int read(){
	char ch=getchar();
	int x=0,f=1;
	while(ch<'0'||ch>'9') {if(ch=='-') f=-1;ch=getchar();}
	while(ch>='0'&&ch<='9') x=x*10+(ch^48),ch=getchar();
	return x*f;
}
void guibing(int l,int r,int flag){
	if(l==r) return;
	int mid=l+r>>1;
	guibing(l,mid,!flag); guibing(mid+1,r,!flag);
	int i=l,j=mid+1,k=l;
	while(i<=mid&&j<=r){
		if(a[i][flag]<=a[j][flag]) a[k++][!flag]=a[i++][flag];
		else a[k++][!flag]=a[j++][flag],ans+=mid-i+1;
	}
	while(i<=mid) a[k++][!flag]=a[i++][flag];
	while(j<=r) a[k++][!flag]=a[j++][flag];
}
int main(){
	scanf("%d",&n);
	for(int i=1;i<=n;i++) a[i][0]=a[i][1]=read();
	guibing(1,n,0);
	printf("%lld",ans);
}

贪心

微扰法


位运算

线性基

\(\mathfrak{talk\; is\; easy\; show\; me\; the\; code}\)

#include<bits/stdc++.h>
using namespace std;
#define int unsigned long long
int n,base[67],a,ans=0;
//大概有些自己明白了
//n是个数,base是指每一位的基底,a是输入的数,ans是答案 
void XXJ(int a){//每输入一个数 
	for(int j=51;~j;j--){//就查输入的数二进制下 最高位
		if(a & (int)1<<j){//的 1 
			if(base[j]) {a^=base[j];}
			//如果这一位已经有了,就异或这一位的数,继续查
			//异或之后,这个数的最高位一定变小了 
			else {base[j]=a;break;}
			//如果没有,就把数存入这一位 
		}	
	}
}
signed main(){
	cin>>n;
	for(int i=1;i<=n;i++){cin>>a;XXJ(a);}
	for(int i=51;~i;i--){
		if(base[i]) //如果这一位有数 
			ans=(ans>(base[i]^ans))?ans:(base[i]^ans);//考虑一个贪心 
	}
	cout<<ans<<endl;
	return 0; 
}

图论


最短路

dijktra

\(\mathfrak{talk\; is\; easy\; show\; me\; the\; code}\)

#include<bits/stdc++.h>
using namespace std;
namespace dij{
	const int N=1e6+105;
	const int INF=0x3f3f3f3f;
	int h[N],cnt,d[N],to[N<<1],nex[N<<1],val[N<<1];
	bool v[N];
	struct qwq{
		int dis,id;
		bool operator < (const qwq &x)const{return x.dis<dis;}
	};
	priority_queue<qwq> q;
	inline void add(int x,int y,int z){
		to[++cnt]=y,val[cnt]=z;
		nex[cnt]=h[x];h[x]=cnt;
	}
	int n,m,s;
	inline void dijkstra(int s){
		memset(d,INF,sizeof(d));
		d[s]=0;q.push((qwq){0,s});
		while(!q.empty()){
			qwq tmp=q.top();q.pop();int x=tmp.id;
			if(!v[x]){
				v[x]=1;
				for(int i=h[x];i;i=nex[i])					
					if(d[to[i]]>d[x]+val[i]){
						d[to[i]]=d[x]+val[i];
						if(!v[to[i]]) q.push((qwq){d[to[i]],to[i]});
					}				
			}
		}
	}
}
using namespace dij;
int main(){
	n=read();m=read();s=read();
	for(int i=1,x,y,z;i<=m;i++){
		x=read();y=read();z=read();
		add(x,y,z);
	}
	dijkstra(s);
	for(int i=1;i<=n;i++) printf("%d ",d[i]);
	return 0;
}

spfa

spfa最短路

\(\mathfrak{talk\; is\; easy\; show\; me\; the\; code}\)

#include<bits/stdc++.h>
using namespace std;
namespace spfa{
	const int N=1e6+105;
	const int INF=0x3f3f3f3f;
	int h[N],cnt,d[N],to[N<<1],nex[N<<1],val[N<<1];
	bool v[N];
	inline void add(int x,int y,int z){
		to[++cnt]=y,val[cnt]=z;
		nex[cnt]=h[x];h[x]=cnt;
	}
	int n,m,s;
	inline void SPFA(int s){
		for(int i=1;i<=n;i++) d[i]=INF;
		queue<int> q;d[s]=0;v[s]=1;q.push(s);
		while(!q.empty()){
			int x=q.front();q.pop();v[x]=0;
			for(int i=h[x];i;i=nex[i]){
				if(d[to[i]]>d[x]+val[i]){
					d[to[i]]=d[x]+val[i];
					if(!v[to[i]]){v[to[i]]=1;q.push(to[i]);}
				}
			}
		}
	}
}
using namespace spfa;
int main(){
	n=read();m=read();s=read();
	for(int i=1,x,y,z;i<=m;i++){
		x=read();y=read();z=read();
		add(x,y,z);
	}	
	SPFA(s);
	for(int i=1;i<=n;i++)printf("%d ",d[i]);
	return 0;
}

负环和差分约束

负环

\(\mathfrak{talk\; is\; easy\; show\; me\; the\; code}\)

#include<bits/stdc++.h>
using namespace std;
const int N=2e3+105,M=6e3+105;
int n,m,h[N],nex[M],to[M],cnt,d[N],tot[N],T,x,y,z,val[M];
bool v[N];
inline void add(int x,int y,int z){
	to[++cnt]=y;val[cnt]=z;
	nex[cnt]=h[x];h[x]=cnt;
}
bool spfa(int s){
	memset(v,0,sizeof(v));memset(d,0x3f,sizeof(d));
	memset(tot,0,sizeof(tot));
	queue<int> q;d[s]=0;v[s]=1;q.push(s);
	while(!q.empty()){
		int x=q.front();q.pop();v[x]=0;
		for(int i=h[x];i;i=nex[i])
			if(d[to[i]]>d[x]+val[i]){
				d[to[i]]=d[x]+val[i];
				if(!v[to[i]]){
					if(++tot[to[i]]>n) return 1;
					q.push(to[i]);v[to[i]]=1;
				}
			}		
	}
	return 0;
}
inline int read(){
	int x=0,f=1;char ch=getchar();
	while(!isdigit(ch)){if(ch=='-')f=-1;ch=getchar();}
	while(isdigit(ch)){x=(x<<3)+(x<<1)+ch-'0';ch=getchar();}
	return x*f;
}
int main(){
	T=read();
	while(T--){
		n=read();m=read();
		for(int i=1;i<=m;i++){
			x=read();y=read();z=read();
			add(x,y,z);if(z>=0)add(y,x,z);
		}
		spfa(1)?puts("YES"):puts("NO");
		memset(h,0,sizeof(h));cnt=0;
	}
	return 0;
}

差分约束

\(\mathfrak{talk\; is\; easy\; show\; me\; the\; code}\)

#include<bits/stdc++.h>
using namespace std;
const int N=2e4+105,M=6e4+105;
//变量名 
int n,m,h[N],nex[M],to[M],v[M],cnt,d[N],tot[N],T,x,y,z;
bool f[N];
//建图 ,to,cnt,nex,v,h
inline void add(int x,int y,int z){
	to[++cnt]=y;v[cnt]=z;
	nex[cnt]=h[x];h[x]=cnt;
} 
//判负环 ,f,d,tot
bool spfa(int s){
	memset(f,0,sizeof(f));memset(d,0x3f,sizeof(d));memset(tot,0,sizeof(tot));
	queue<int> q;
	d[s]=0;f[s]=1;q.push(s);
	while(!q.empty()){
		int x=q.front();q.pop();f[x]=0;
		for(int i=h[x];i;i=nex[i])
			if(d[to[i]]>d[x]+v[i]){
				d[to[i]]=d[x]+v[i];
				if(!f[to[i]]){
					if(++tot[to[i]]>n) return 1;//有负环->无解 
					q.push(to[i]);f[to[i]]=1;
				}
			}
	}
	return 0;
}	

int main(){
	scanf("%d%d",&n,&m);
	for(int i=1;i<=n;i++) add(0,i,0);
	for(int i=1;i<=m;i++){
		scanf("%d%d%d",&x,&y,&z);
		add(y,x,z);//这里注意 
	}
	if(spfa(0))printf("NO");
	else for(int i=1;i<=n;i++)printf("%d ",d[i]);
	return 0;
} 

floyd

分层最短路

\(\mathfrak{talk\; is\; easy\; show\; me\; the\; code}\)

#include<bits/stdc++.h>
using namespace std;

inline int read(){
	int x=0;char c=getchar();
	while(!isdigit(c))c=getchar();
	while(isdigit(c)){
		x=x*10+c-'0';
		c=getchar();
	}
	return x;
}

const int N=110005;

int n,m,k;
int head[N],cnt;
int dis[N];
bool vis[N];

struct Node{
	int to,nxt,val;
}e[2500001];

void add(int u,int v,int w){
	e[++cnt]=(Node){v,head[u],w};
	head[u]=cnt;
}
priority_queue<pair<int,int> > q;

void dij(int s){
	memset(dis,0x3f,sizeof dis);
	memset(vis,0,sizeof vis);
	dis[s]=0;
	q.push(make_pair(0,s));
	while(!q.empty()){
		int u=q.top().second;
		q.pop();
		if(!vis[u]){
			vis[u]=1;
			for(int i=head[u];i;i=e[i].nxt){
				int to=e[i].to;
				if(dis[to]>dis[u]-e[i].val){
					dis[to]=dis[u]-e[i].val;
					q.push(make_pair(-dis[to],to));
				}
			}
		}
	}
}

int main(){
	int n=read(),m=read(),k=read(),s=read(),t=read();
	for(int i=0,u,v,w;i<m;i++){
		u=read(),v=read(),w=read();
		add(u,v,-w);
		add(v,u,-w);
		for(int j=1;j<=k;j++){
			add(u+(j-1)*n,v+j*n,0);
			add(v+(j-1)*n,u+j*n,0);
			add(u+j*n,v+j*n,-w);
			add(v+j*n,u+j*n,-w);
		}
	}
	for(int i=1;i<=k;i++){
		add(t+(i-1)*n,t+i*n,0);
	}
	dij(s);
	cout<<dis[t+k*n];
	return 0;
}

线段树优化建图


最小生成树

\(\mathfrak{talk\; is\; easy\; show\; me\; the\; code}\)

#include<bits/stdc++.h>
using namespace std;
int n,m,x,y,cnt,f[200105];
struct edge{int fro,to;long long v;}e[200105];
inline bool cmp(edge x,edge y){return x.v<y.v;}
int find(int x){return f[x]==x?x:f[x]=find(f[x]);}
inline void Kruskal(long long &ans){
	for(int i=0;i<n;i++)f[i]=i;sort(e,e+m,cmp);
	for(int i=0;i<m;i++){
		x=find(e[i].fro);y=find(e[i].to);
		if(x!=y){ans+=e[i].v;f[x]=y;cnt++;}
		if(cnt==n-1) break;
	}
	if(cnt<n-1) {printf("orz");exit(0);}
}
int main(){
	cin>>n>>m;long long ans=0;
	for(int i=0;i<m;i++)scanf("%d%d%d",&e[i].fro,&e[i].to,&e[i].v);
	Kruskal(ans);printf("%lld",ans);
	return 0;
}

LCA

树的直径

倍增LCA

dfs写法

\(\mathfrak{talk\; is\; easy\; show\; me\; the\; code}\)

#include<bits/stdc++.h>
using namespace std;
const int N=5e5+105;
int to[N<<1],nex[N<<1],h[N],cnt,d[N],f[N][27],lg[N],n,m,s;
void add(int x,int y){to[++cnt]=y;nex[cnt]=h[x];h[x]=cnt;}
void pre(){for(int i=2;i<=n;i++)lg[i]=lg[i>>1]+1;}
void dfs(int x,int fa){
	f[x][0]=fa;d[x]=d[fa]+1;
	for(int i=1;i<=19;i++)f[x][i]=f[f[x][i-1]][i-1];
	for(int i=h[x];i;i=nex[i])if(to[i]!=fa)dfs(to[i],x);
}
inline int LCA(int x,int y){
	if(d[x]<d[y])swap(x,y);
	while(d[x]>d[y])x=f[x][lg[d[x]-d[y]]];
	if(x==y)return x;
	for(int i=lg[d[x]];i>=0;i--)if(f[x][i]!=f[y][i])x=f[x][i],y=f[y][i];
	return f[x][0];
} 
int main(){
	scanf("%d%d%d",&n,&m,&s);
	for(int i=1,x,y;i<n;i++){scanf("%d%d",&x,&y);add(x,y);add(y,x);}
	pre();dfs(s,0);
	for(int i=1,x,y;i<=m;i++){scanf("%d%d",&x,&y);;printf("%d\n",LCA(x,y));}
	return 0;
}

bfs写法

\(\mathfrak{talk\; is\; easy\; show\; me\; the\; code}\)


树链剖分的LCA

\(\mathfrak{talk\; is\; easy\; show\; me\; the\; code}\)

#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
#define re register
const ll maxn = 5e5+5;
int n,m,s;
int head[maxn],nxt[maxn<<1],to[maxn<<1],cnt;
inline void add(int u,int v){nxt[++cnt]=head[u];to[cnt]=v;head[u]=cnt;}
int fa[maxn],son[maxn],dep[maxn],size[maxn];
int top[maxn];
void dfs1(int u,int f){
	fa[u]=f;dep[u]=dep[f]+1;size[u]=1;
	for(re int i=head[u];i;i=nxt[i]){
		if(to[i]==f)continue;
		dfs1(to[i],u);size[u]+=size[to[i]];
		if(size[to[i]]>size[son[u]])son[u]=to[i];
	}
}
void dfs2(int u,int f){
	top[u]=f;
	if(son[u]==0)return;
	dfs2(son[u],f);
	for(re int i=head[u];i;i=nxt[i]){
		if(to[i]==son[u]||to[i]==fa[u])continue;
		dfs2(to[i],to[i]);
	}
}
inline int lca(int u,int v){
	while(top[u]!=top[v]){
		if(dep[top[u]]<dep[top[v]])swap(u,v);
		u=fa[top[u]];
	}
	return dep[u]<dep[v]?u:v;
}
const int MA = 1 << 23;
char buf[MA], *p1 = buf, *p2 = buf;
#define gc()                                                            \
    (p1 == p2 && (p2 = (p1 = buf) + fread(buf, 1, MA, stdin), p1 == p2) \
         ? EOF                                                          \
         : *p1++)

inline ll read(){
    ll x=0,f=1;char ch=gc();
    while(!isdigit(ch)){if(ch=='-')f=-1;ch=gc();}
    while(isdigit(ch)){x=x*10+(ch^48);ch=gc();}
    return x*f;
}
int main() {
	n=read();m=read();s=read();
	for(re int i=1,u,v;i<n;i++){
		u=read();v=read();
		add(u,v);add(v,u);
	}
	dfs1(s,0); dfs2(s,s);	
	while(m--){
		int u,v;
		u=read();v=read();
		printf("%d\n",lca(u,v));
	}
	return 0;
}


树上差分


Tarjan算法

缩点

给定一个 \(n\) 个点 \(m\) 条边有向图,每个点有一个权值,求一条路径,使路径经过的点权值之和最大。你只需要求出这个权值和。

允许多次经过一条边或者一个点,但是,重复经过的点,权值只计算一次。

\(\mathfrak{talk\; is\; easy\; show\; me\; the\; code}\)

#include<bits/stdc++.h>
using namespace std;
const int N=1e5+105,M=5e5+105;
int h[M],fro[M],to[M],nex[M],cnt,val[N],d[N],v[N];
int dfn[N],low[N],num,c[N],tot,f[N],n,m,ans=-1e9;
vector<int>G[N];
stack<int>t;
inline void add(int x,int y){nex[++cnt]=h[x];h[x]=cnt;to[cnt]=y;fro[cnt]=x;}
void tarjan(int x){
	dfn[x]=low[x]=++num;t.push(x);c[x]=1;
	for(int i=h[x];i;i=nex[i])
		if(!dfn[to[i]]){tarjan(to[i]);low[x]=min(low[x],low[to[i]]);}
		else if(c[to[i]]){low[x]=min(low[x],dfn[to[i]]);}
	if(low[x]==dfn[x]){
		tot++;
		while(1){
			int y=t.top();t.pop();
			v[y]=tot;d[tot]+=val[y];c[y]=0;
			if(x==y)break;
		}
	}
}
void dfs(int x){
	if(f[x])return ;
	int ms=0;f[x]=d[x];
	for(int i=0;i<G[x].size();i++){if(!f[G[x][i]])dfs(G[x][i]);ms=max(ms,f[G[x][i]]);}
	f[x]+=ms;
}
void work(){
	for(int i=1,x,y;i<=cnt;i++)
		if(v[fro[i]]!=v[to[i]]){x=v[fro[i]],y=v[to[i]];G[x].push_back(y);}
	for(int i=1;i<=tot;i++)if(!f[i]){dfs(i);ans=max(ans,f[i]);}		
}
int main(){
	scanf("%d%d",&n,&m);
	for(int i=1;i<=n;i++)scanf("%d",&val[i]);
	for(int i=1,x,y;i<=m;i++){scanf("%d%d",&x,&y);add(x,y);}
	for(int i=1;i<=n;i++)if(!dfn[i])tarjan(i);
	work();
	printf("%d",ans);
	return 0;
}

割点割边

割点

无向连通图中,如果删除某点后,图变成不连通,则称该点为割点。

若当前点为树根的时候,为割点的条件是:有两颗及以上的子树

若当前节点不是树根的时候,条件是 low[y]>=dfn[x],就是在 x之后遍历的点,能够向上翻,最多到x,

\(\mathfrak{talk\; is\; easy\; show\; me\; the\; code}\)

#include<bits/stdc++.h>
using namespace std;
const int N=2e4+107,M=2e5+107;
int h[N],cnt,to[M],nex[M],n,m,dfn[N],low[N],num,ans;
bool cut[N];
inline void add(int x,int y){to[++cnt]=y;nex[cnt]=h[x];h[x]=cnt;}
void tarjan(int x,int f){
	int p=0;dfn[x]=low[x]=++num;
	for(int i=h[x];i;i=nex[i]){
		if(!dfn[to[i]]){
			tarjan(to[i],f);low[x]=min(low[x],low[to[i]]);
			if(low[to[i]]>=dfn[x]&&x!=f)cut[x]=1;
			if(x==f)p++;
		}
		low[x]=min(low[x],dfn[to[i]]);
	}
	if(x==f && p>=2)cut[f]=1;
}
int main(){
	cin>>n>>m;
	for(int i=1,u,v;i<=m;i++){cin>>u>>v;add(u,v);add(v,u);}
	for(int i=1;i<=n;i++)if(!dfn[i])tarjan(i,i);
	for(int i=1;i<=n;i++)if(cut[i])ans++;
	cout<<ans<<endl;
	for(int i=1;i<=n;i++)if(cut[i])cout<<i<<" ";
	return 0;
}

割边/桥

点分治

给定一棵有 n 个点的树,询问树上距离为 k 的点对是否存在。

\(\mathfrak{talk\; is\; easy\; show\; me\; the\; code}\)

#include<bits/stdc++.h>
#define re register
#define INF 0x3f3f3f3f
using namespace std;
const int maxn=2e5+5;
int n,m,ask[maxn],d[maxn],t[maxn],r[maxn],q[maxn],h[maxn],to[maxn],nex[maxn],v[maxn],cnt,siz[maxn],mx[maxn],rt,sum; 
bool pd[INF],vis[maxn];
inline void add(int x,int y,int val){
	nex[++cnt]=h[x];h[x]=cnt;
	to[cnt]=y;v[cnt]=val;	
}
inline int max(int a,int b){return a>b?a:b;}
void zzx(int u,int f){
	siz[u]=1;mx[u]=0;
	for(re int i=h[u];i;i=nex[i]){
		if(to[i]==f||vis[to[i]])continue;
		zzx(to[i],u);
		siz[u]+=siz[to[i]];mx[u]=max(mx[u],siz[to[i]]);
	}
	mx[u]=max(mx[u],sum-siz[u]);
	if(mx[u]<mx[rt])rt=u;
} 
void zjl(int u,int f){
	r[++r[0]]=d[u];
	for(re int i=h[u];i;i=nex[i]){
		if(to[i]==f||vis[to[i]])continue;
		d[to[i]]=d[u]+v[i];
		zjl(to[i],u);
	}
}
void calc(int u){
	int cnt=0;
	for(re int i=h[u];i;i=nex[i]){
		if(vis[to[i]])continue;
		r[0]=0;d[to[i]]=v[i];
		zjl(to[i],u);
		for(re int j=r[0];j>=1;j--)for(re int k=1;k<=m;k++)if(ask[k]>=r[j])t[k]|=pd[ask[k]-r[j]];
		for(re int j=r[0];j>=1;j--){pd[r[j]]=1;q[++cnt]=r[j];}
	}
	for(re int i=1;i<=cnt;i++)pd[q[i]]=0;
}
void solve(int u){
	vis[u]=q;pd[0]=1;calc(u);
	for(re int i=h[u];i;i=nex[i]){
		if(vis[to[i]])continue;
		sum=siz[to[i]];mx[rt=0]=INF;
		zzx(to[i],0);solve(rt);
	}
}
inline int read(){int x=0,f=1;char ch=getchar();while(!isdigit(ch)){if(ch=='-')f=-1;ch=getchar();}while(isdigit(ch)){x=x*10+(ch^48);ch=getchar();}return x*f;}
int main(){
	n=read();m=read();
	for(re int i=1,u,v,val;i<n;i++){
		u=read();v=read();val=read();
		add(u,v,val);add(v,u,val);		
	}
	for(re int i=1;i<=m;i++)ask[i]=read();
	mx[rt]=INF;sum=n;
	zzx(1,0);
	solve(rt);
	for(re int i=1;i<=m;i++){
		if(t[i])printf("AYE\n");
		else printf("NAY\n");
	}
	return 0;
}

给定一棵 n 个节点的树,每条边有边权,求出树上两点距离小于等于 k 的点对数量。

\(\mathfrak{talk\; is\; easy\; show\; me\; the\; code}\)

#include<bits/stdc++.h>
#define re register
#define INF 0x3f3f3f3f
using namespace std;
const int maxn=2e5+5;
int n,m,ask[maxn],d[maxn],t[maxn],r[maxn],q[maxn],h[maxn],to[maxn],nex[maxn],v[maxn],cnt,siz[maxn],mx[maxn],rt,sum,k,ans; 
bool pd[INF],vis[maxn],flag;
inline void add(int x,int y,int val){
	nex[++cnt]=h[x];h[x]=cnt;
	to[cnt]=y;v[cnt]=val;	
}
inline int max(int a,int b){return a>b?a:b;}
void zzx(int u,int f){
	siz[u]=1;mx[u]=0;
	for(re int i=h[u];i;i=nex[i]){
		if(to[i]==f||vis[to[i]])continue;
		zzx(to[i],u);
		siz[u]+=siz[to[i]];mx[u]=max(mx[u],siz[to[i]]);
	}
	mx[u]=max(mx[u],sum-siz[u]);
	if(mx[u]<mx[rt])rt=u;
} 
void zjl(int u,int f){
	r[++r[0]]=d[u];
	for(re int i=h[u];i;i=nex[i]){
		if(to[i]==f||vis[to[i]])continue;
		d[to[i]]=d[u]+v[i];
		zjl(to[i],u);
	}
}
int calc(int u,int val){
    r[0]=0;d[u]=val;zjl(u,0);
    sort(r+1,r+1+r[0]);
    int le=1,ri=r[0],res=0;
    while(le<=ri){if(r[le]+r[ri]<=k) res+=ri-le,le++;else ri--;}
	return res;
}
void solve(int u){
	vis[u]=1;ans+=calc(u,0);
	for(re int i=h[u];i;i=nex[i]){
		if(vis[to[i]])continue;
		ans-=calc(to[i],v[i]);sum=siz[to[i]];mx[rt=0]=n;
		zzx(to[i],u);solve(rt);
	}
}
inline int read(){int x=0,f=1;char ch=getchar();while(!isdigit(ch)){if(ch=='-')f=-1;ch=getchar();}while(isdigit(ch)){x=x*10+(ch^48);ch=getchar();}return x*f;}
int main(){
	n=read();
	for(re int i=1,u,v,val;i<n;i++){
		u=read();v=read();val=read();
		add(u,v,val);add(v,u,val);		
	}
	k=read();
	mx[rt]=sum=n;
	zzx(1,0);
	solve(rt);
	printf("%d",ans);
	return 0;
}

二分图匹配

\(\mathfrak{talk\; is\; easy\; show\; me\; the\; code}\)

#include<bits/stdc++.h>
using namespace std;
const int N=5e5+105;
int h[N],to[N],nex[N],cnt,n,m,e,ans,match[N];
bool v[N];
inline void add(int x,int y){to[++cnt]=y;nex[cnt]=h[x];h[x]=cnt;}
bool dfs(int x){
	for(int i=h[x];i;i=nex[i])
		if(!v[to[i]]){
			v[to[i]]=1;
			if(!match[to[i]]||dfs(match[to[i]])) {match[to[i]]=x;return 1;}	
		}
	return 0;
}
int main(){
	cin>>n>>m>>e;
	for(int i=1,x,y;i<=e;i++){
		cin>>x>>y;
		add(x,y+n);add(y+n,x);
	}
	for(int i=1;i<=n;i++){
		memset(v,0,sizeof(v));
		if(dfs(i)) ans++;
	}
	cout<<ans;
	return 0;
}

网络流初步

最大流

\(\mathfrak{talk\; is\; easy\; show\; me\; the\; code}\)

#include<bits/stdc++.h>
#define int long long
const int maxn = 10001, maxm = 200005;
int n = 0, m = 0, s = 0, t = 0;
int head[maxn], nxt[maxm], to[maxm], val[maxm], cnt = 1;
inline void insert(int u, int e, int v) { nxt[++cnt] = head[u]; head[u] = cnt; to[cnt] = e; val[cnt] = v; }
bool vis[maxn];
int d[maxn];
bool bfs() {
	memset(vis, 0, sizeof(vis));
	std::queue<int> q;
	q.push(s); vis[s] = true;
	d[s] = 1;
	while (!q.empty()) {
		int x = q.front(); q.pop();
		for (int i = head[x]; i; i = nxt[i])
			if (!vis[to[i]] && val[i]) {
				d[to[i]] = d[x] + 1;
				if (to[i] == t) return true;
				vis[to[i]] = true;
				q.push(to[i]);
			}
	}
	return false;
}
inline int min(int a, int b) { return a < b ? a : b; }
int dinic(int x, int flow) {
	if (x == t) return flow;
	int use = 0, k = 0;
	for (int i = head[x]; i; i = nxt[i]) {
		if (val[i] && d[to[i]] == d[x] + 1) {
			k = dinic(to[i], min(flow - use, val[i]));
			if (!k) d[to[i]] = 0;
			val[i] -= k;
			val[i ^ 1] += k;
			use += k;
		}
	}
	return use;
}
signed main() {
	scanf("%lld%lld%lld%lld", &n, &m, &s, &t);
	int a = 0, b = 0, c = 0;
	for (int i = 1; i <= m; ++i) {
		scanf("%lld%lld%lld", &a, &b, &c);
		insert(a, b, c); insert(b, a, 0);
	}
	int ans = 0;
	while (bfs())
		ans += dinic(s, 0x7fffffff);
	printf("%lld", ans);
	return 0;
}

最小费用最大流

\(\mathfrak{talk\; is\; easy\; show\; me\; the\; code}\)

//#define LawrenceSivan

#include <bits/stdc++.h>
using namespace std;

typedef long long ll;
typedef unsigned long long ull;

#define INF 0x3f3f3f3f3f3f3f3f
#define re register
#define int ll

const int maxn=5e3+5;
const int maxm=5e4+5;

int n,m,s,t;

int head[maxn],to[maxm<<1],nxt[maxm<<1],flow[maxm<<1],cost[maxm<<1],cnt=1;

int maxflow,mincost;

inline void add(int u,int v,int _flow,int _cost){
    nxt[++cnt]=head[u];to[cnt]=v;flow[cnt]=_flow;
    cost[cnt]=_cost;head[u]=cnt;

    nxt[++cnt]=head[v];to[cnt]=u;flow[cnt]=0;
    cost[cnt]=-_cost;head[v]=cnt;
}

int incf[maxn];

int dis[maxn],pre[maxn];

bool vis[maxn];

queue <int> q;

inline bool SPFA(){
    memset(dis,0x3f,sizeof(dis));
    memset(vis,false,sizeof(vis));

    q.push(s),dis[s]=0;vis[s]=true;incf[s]=INF;
    while(!q.empty()){
        int u=q.front();q.pop();vis[u]=false;
        for(re int i=head[u];i;i=nxt[i]){
            int v=to[i];
            if(!flow[i])continue;
            if(dis[v]>dis[u]+cost[i]){
                dis[v]=dis[u]+cost[i];
                incf[v]=min(incf[u],flow[i]);
                pre[v]=i;
                if(!vis[v])q.push(v),vis[v]=true;
            }   
        }
    }
    if(dis[t]==INF)return false;
    return true;
}

inline void MCMF(){
    while(SPFA()){
        int x=t;
        maxflow+=incf[t];
        mincost+=incf[t]*dis[t];
        while(x!=s){
            int i=pre[x];
            flow[i]-=incf[t];
            flow[i^1]+=incf[t];
            x=to[i^1];
        }
    }
}

namespace IO{
    template<typename T>
    inline void read(T &x){
        x=0;T f=1;char ch=getchar();
        while (!isdigit(ch)) {if(ch=='-')f=-1;ch=getchar();}
        while (isdigit(ch)){x=x*10+(ch^48);ch=getchar();}
        x*=f;
    }

    template <typename T, typename... Args>
    inline void read(T& t, Args&... args) {
        read(t); read(args...);
    }

    template<typename T>
    void write(T x){
        if(x<0)putchar('-'),x=-x;
        if(x>9)write(x/10);
        putchar(x%10+'0');
    }

    template<typename T,typename... Args>
    void write(T t,Args... args){
        write(t);putchar(' ');write(args...);
    }
}

using namespace IO;

signed main() {
#ifdef LawrenceSivan
    freopen("aa.in","r", stdin);
    freopen("aa.out","w", stdout);
#endif  
    read(n,m,s,t);
    for(re int i=1,u,v,f,c;i<=m;i++){
        read(u,v,f,c);
        add(u,v,f,c);
    }

    MCMF();

    write(maxflow,mincost),puts("");


    return 0;
}


数论


素数相关

线性筛素数

\(\mathfrak{talk\; is\; easy\; show\; me\; the\; code}\)

#include<bits/stdc++.h>
using namespace std;
bool v[100000005];
int n,q,k,cnt;
int p[10000005];
void prim(int n){
	for(int i=2;i<=n;i++){
		v[1]=1;
		if(v[i]==0) p[cnt++]=i;			
		for(int j=0;j<cnt&&p[j]*i<=n;j++){v[i*p[j]]=1;if(i%p[j]==0) break;}						
	}
}

int main(){
	std::ios::sync_with_stdio(0);
	cin>>n>>q;
	prim(n);//质数的英文怎么拼写来着 
	while(q--){
		cin>>k;
		cout<<p[k-1]<<endl;
	}
	return 0;
}

欧几里得

裴蜀定理

给定一个包含 nnn 个元素的整数序列 AAA,记作 A1,A2,A3,...,AnA_1,A_2,A_3,...,A_nA1​,A2​,A3​,...,An​。

求另一个包含 nnn 个元素的待定整数序列 XXX,记 S=∑i=1nAi×XiS=\sum\limits_{i=1}^nA_i\times X_iS=i=1∑n​Ai​×Xi​,使得 S>0S>0S>0 且 SSS 尽可能的小。

\(\mathfrak{talk\; is\; easy\; show\; me\; the\; code}\)

#include<bits/stdc++.h>
using namespace std;
int n,s,x,y;
int gcd(int a,int b){return b==0?a:gcd(b,a%b);}
int main(){
	cin>>n;
	if(n==1){
		cin>>x;
		if(x<0)x=-x;
		cout<<x;
	}
	if(n>=2){
		cin>>x>>y;
		if(x<0)x=-x;
		if(y<0)y=-y;
		s=gcd(x,y);
	}
	n-=2;
	while(n--){
		cin>>x;
		if(x<0)x=-x;
		s=gcd(s,x);
	}
	cout<<s;
	return 0;
}

欧几里得算法(gcd)

\(\mathfrak{talk\; is\; easy\; show\; me\; the\; code}\)


扩展欧几里得算法

\(\mathfrak{talk\; is\; easy\; show\; me\; the\; code}\)

//同余方程:ax+by=c
#include<bits/stdc++.h>
#define ll long long
using namespace std;
ll a,b,c,x,y;
void exgcd(ll a,ll b,ll &x,ll &y){
	if(b==0) x=1,y=0;
	else exgcd(b,a%b,y,x),y-=a/b*x;
}
ll gcd(ll a,ll b){return b==0?a:gcd(b,a%b);}
int main(){
	cin>>a>>b;c=1;
	ll GCD=gcd(a,b);
	if(c%GCD!=0) cout<<"Orz,I cannot find x!";
	else{
		exgcd(a,b,x,y);
        x=(x+b)%b;
		ll ans=((c/GCD)*x+(b/GCD))%(b/GCD);
        cout<<ans<<endl;
	}
	return 0;
}

欧拉定理

欧拉定理

\(\mathfrak{talk\; is\; easy\; show\; me\; the\; code}\)


扩展欧拉定理

\(\mathfrak{talk\; is\; easy\; show\; me\; the\; code}\)

//扩展欧拉定理,求a^b mod m
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
ll a,m,b;
int phi(int x){
	int ans=1,num=1;
	for(int i=2;i*i<=x;i++)
		if(!(x%i)){
			num=i-1,x/=i;
			while(!(x%i)) num=num*i,x/=i;
			ans=num*ans;
		}
	if(x!=1) ans=ans*(x-1);
	return ans;
}
inline ll read(ll m){
	ll x=0,f=0;char ch=getchar();
	while(!isdigit(ch)) ch=getchar();
	while(isdigit(ch)){
		x=x*10+ch-'0';
		if(x>=m) f=1;
		x%=m;ch=getchar();
	}
	return x+(f==1?m:0);
}
inline ll qp(ll a,ll b,ll p){
	ll ans=1;
	while(b){
		if(b&1) ans=ans*a%p;
		a=a*a%p;b>>=1;		
	}
	return ans%p;
}
int main(){
	cin>>a>>m;b=read(phi(m));
	cout<<qp(a,b,m);
	return 0;
}

无穷幂

\[2^{2^{2^{...}}}\mod p \]

\(\mathfrak{talk\; is\; easy\; show\; me\; the\; code}\)

#include<bits/stdc++.h>
#define int long long
using namespace std;
const int N=1e7+105,M=1e6+105;
int n,cnt,p[M],phi[N],T,P;
bool v[N];
void xxs(int n){
    phi[1]=1;
    for(int i=2;i<=n;i++){
        if(!v[i])p[++cnt]=i,phi[i]=i-1;
        for(int j=1;j<=cnt&&i*p[j]<=n;j++){
            v[i*p[j]]=1;
            if(i%p[j]==0){phi[i*p[j]]=phi[i]*p[j];break;}
            else phi[i*p[j]]=phi[i]*phi[p[j]];
        }
    }
}
int ksm(int a,int b,int p){
    int res=1;
    while(b){
        if(b&1) res=res*a%p;
        a=a*a%p;b=b>>1;
    }
    return res%p;
}
int work(int p){
    if(p==1) return 0;
    return ksm(2,work(phi[p])+phi[p],p);
}
inline int read(){
    int x=0,f=1;char ch=getchar();
    while(!isdigit(ch)){if(ch=='-')f=-1;ch=getchar();}
    while(isdigit(ch)){x=(x<<3)+(x<<1)+ch-'0';ch=getchar();}
    return x*f;
}
signed main(){
    xxs(N-105);T=read();
    while(T--){P=read();printf("%lld\n",work(P));}
    return 0;
}

中国剩余定理(CRT)

中国剩余定理

\(\mathfrak{talk\; is\; easy\; show\; me\; the\; code}\)

//中国剩余定理
#include<bits/stdc++.h>
#define ll long long
using namespace std;
const int N=21;
ll m[N],a[N],M=1,n,ans;
void exgcd(ll a,ll b,ll &x,ll &y){
    if(!b) x=1,y=0;
    else exgcd(b,a%b,y,x),y-=a/b*x;
}
int main(){
    cin>>n;
    for(int i=1;i<=n;i++){cin>>m[i];M*=m[i];cin>>a[i];}
    for(int i=1;i<=n;i++){
        ll mi=M/m[i],x=0,y=0;
        exgcd(mi,m[i],x,y);
        ans+=a[i]*mi*(x<0?x+m[i]:x);
    }
    cout<<(ans+M)%M;
    return 0;
}

扩展中国剩余定理

\(\mathfrak{talk\; is\; easy\; show\; me\; the\; code}\)


//扩展中国剩余定理(EXCRT)
#include<bits/stdc++.h>
#define ll long long
using namespace std;
ll a[100105],b[100105],ans,m,x,y,n;
ll EXGCD(ll a,ll b,ll &x,ll &y){
    ll ans;
    if(b==0){x=1,y=0;return a;}
    ans=EXGCD(b,a%b,y,x);y-=a/b*x;
    return ans;
}
ll mymul(ll n,ll k,ll mod){
    ll ans=0;
    while(k){
        if(k&1) ans=(ans+n)%mod;
        n=(n+n)%mod;k>>=1;
    }
    return ans;
}//龟速乘
int main(){
    cin>>n;
    for(int i=1;i<=n;i++) cin>>b[i]>>a[i];
    ans=a[1],m=b[1];
    for(int i=2;i<=n;i++){
        ll B=((a[i]-ans)%b[i]+b[i])%b[i];
        ll GCD=EXGCD(m,b[i],x,y);
        x=mymul(x,B/GCD,b[i]);
        ans+=m*x;
        m*=b[i]/GCD;
        ans=(ans+m)%m;
    }
    cout<<ans;
    return 0;
}

扩展扩展中国剩余定理

\(\mathfrak{talk\; is\; easy\; show\; me\; the\; code}\)

#include<bits/stdc++.h>
#define int long long
using namespace std;
const int N=1e5+105;
int t,n,m,a[N],p[N],j[N],x,y,G,b[N],mx,c;
multiset<int> g;
inline int gcd(int a,int b){return b==0?a:gcd(b,a%b);}
void exgcd(int a,int b,int &x,int &y){
	if(b==0){x=1,y=0;G=a;return ;}
	exgcd(b,a%b,y,x);y-=a/b*x;
}
inline int mul(int a,int b,int p){
	int ans=0;
	while(b){
		if(b&1)ans=(ans+a)%p;
		a=(a+a)%p;b>>=1; 
	}
	return ans;
}
inline int read(){
	int x=0,f=1;char ch=getchar();
	while(!isdigit(ch)){if(ch=='-')f=-1;ch=getchar();}
	while(isdigit(ch)){x=(x<<3)+(x<<1)+ch-'0';ch=getchar();}
	return x*f;
}
signed main(){
	t=read();
	outdoor:
	while(t--){
		n=read(),m=read();mx=c=0;
		for(int i=1;i<=n;i++)a[i]=read();
		for(int i=1;i<=n;i++)p[i]=read();
		for(int i=1;i<=n;i++)j[i]=read();
		for(int i=1;i<=m;i++){int x=read();g.insert(x);}
		m=1;
		for(int i=1;i<=n;i++){
			multiset<int>::iterator pos;
			pos=g.upper_bound(a[i]); if(pos!=g.begin())pos--;
			b[i]=*pos;
			g.erase(pos);g.insert(j[i]);
			mx=max(mx,(a[i]-1)/b[i]+1);
			b[i]%=p[i];a[i]%=p[i];
			if(!b[i] && a[i]) {cout<<"-1";goto outdoor;}
			if(!b[i] && !a[i])continue;
			exgcd(b[i],p[i],x,y);
			if(a[i]%G){cout<<"-1";goto outdoor;}
			p[i]/=G;a[i]=mul(a[i]/G,(x%p[i]+p[i])%p[i],p[i]);
			exgcd(m,p[i],x,y);
			if((a[i]-c)%G){cout<<"-1";goto outdoor;}
			m=m/G*p[i];
			c=(c+mul(mul(m/p[i],((a[i]-c)%m+m)%m,m),(x%m+m)%m,m))%m;
		}
		int ANS=c>mx?c:c+m*(mx-c-1)/m+1;
		cout<<ANS<<endl;
		g.clear();
	}
	return 0;
}

卢卡斯

卢卡斯定理

\[C^{n}_{n+m} \mod\; p \]

\(\mathfrak{talk\; is\; easy\; show\; me\; the\; code}\)

//卢卡斯定理
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int maxn=100010;
ll a[maxn],p;
ll mypow(ll y,ll z,int p){
	ll ans=1;
	while(z){
		if(z&1)ans=ans*y%p;
		y=y*y%p;z>>=1;		
	}
	return ans%p;
}
ll c(ll n,ll m){
	if(m>n)return 0;
	return ((a[n]*mypow(a[m],p-2,p))%p*mypow(a[n-m],p-2,p)%p);
}
ll lucas(ll n,ll m){
	if(!m)return 1;
	return c(n%p,m%p)*lucas(n/p,m/p)%p;
}
inline ll read(){
    ll x=0,f=1;char ch=getchar();
    while (!isdigit(ch)) {if (ch=='-') f=-1;ch=getchar();}
    while (isdigit(ch)) x=x*10+ch-'0',ch=getchar();
    return x*f;
}
int main(){
	int t=read();
	while(t--){
		int n=read();int m=read();p=read();
		a[0]=1;	
		for(int i=1;i<=p;i++){a[i]=(a[i-1]*i)%p;}
		cout<<lucas(n+m,n)<<endl;
	}
	return 0;
}

扩展卢卡斯定理

\[C^m_n\mod p \]

\(\mathfrak{talk\; is\; easy\; show\; me\; the\; code}\)

#include<bits/stdc++.h>
#define ll long long
using namespace std;
ll A[1001],B[1001];
void exgcd(ll a,ll b,ll &x, ll &y){
	if(b==0){x=1,y=0;return;}
	exgcd(b,a%b,y,x);y-=a/b*x;	
} 
ll gcd(ll a,ll b){return b==0?a:gcd(b,a%b);}
inline ll inv(ll a,ll p){
	ll x,y;exgcd(a,p,x,y);
	return (x+p)%p;
}
inline ll lcm(ll a,ll b){return a/gcd(a,b)*b;}
inline ll myabs(ll x){return x?x:-x;}
inline ll fmul(ll a,ll b,ll p){
	ll t=0;a%=p;b%=p;
	while(b){
		if(b&1ll)t=t+a%p;
		b>>=1ll;a=a+a%p;		
	}
	return t;
}
inline ll fpow(ll a,ll b,ll p){
	ll res=1;
	while(b){
		if(b&1ll)res=res*a%p;
		a=a*a%p;b>>=1ll;		
	}
	return res%p;
}
inline ll read(){
    ll x=0,f=1;char ch=getchar();
    while (!isdigit(ch)) {if (ch=='-') f=-1;ch=getchar();}
    while (isdigit(ch)) x=x*10+ch-'0',ch=getchar();
    return x*f;
}
inline ll F(ll n,ll p,ll pk){
	if(n==0)return 1;
	ll rou=1;ll rem=1;
	for(ll i=1;i<=pk;i++){if(i%p)rou=rou*i%pk;}	
	rou=fpow(rou,n/pk,pk);
	for(ll i=pk*(n/pk);i<=n;i++){if(i%p)rem=rem*(i%pk)%pk;}
	return F(n/p,p,pk)*rou%pk*rem%pk;	
}
inline ll G(ll n,ll p){
	if(n<p)return 0;
	return G(n/p,p)+(n/p);
}
inline ll c_pk(ll n,ll m,ll p,ll pk){
	ll fz=F(n,p,pk),fm1=inv(F(m,p,pk),pk),fm2=inv(F(n-m,p,pk),pk);
	ll mi=fpow(p,G(n,p)-G(m,p)-G(n-m,p),pk);
	return fz*fm1%pk*fm2%pk*mi%pk;
}
inline ll exlucas(ll n,ll m,ll p){
	ll lcj=p,tot=0;
	for(ll tmp=2;tmp*tmp<=p;tmp++)
		if(!(lcj%tmp)){
			ll pk=1;
			while(!(lcj%tmp)){pk*=tmp;lcj/=tmp;}
			A[++tot]=pk,B[tot]=c_pk(n,m,tmp,pk);	
	
		}	
	if(lcj!=1){A[++tot]=lcj;B[tot]=c_pk(n,m,lcj,lcj);}	
	ll ans=0;
	for(ll i=1;i<=tot;i++){
		ll M=p/A[i],T=inv(M,A[i]);
		ans=(ans+B[i]*M%p*T%p)%p;
	}
	return ans;
}
int main(){
	ll n=read();ll m=read();ll p=read();
	printf("%lld\n",exlucas(n,m,p));
	return 0;
} 

BSGS

给定一个质数 ppp,以及一个整数 bbb,一个整数 nnn,现在要求你计算一个最小的非负整数 lll,满足 bl≡n(modp)b^l \equiv n \pmod pbl≡n(modp)

\(\mathfrak{talk\; is\; easy\; show\; me\; the\; code}\)

#include<bits/stdc++.h>
#define int long long
using namespace std;
int a,b,p;
int ksm(int a,int b,int p){
	int ans=1;
	while(b>0){
		if(b&1) ans=ans*a%p;
		a=a*a%p;
		b>>=1;
	}
	return ans;
}
int BSGS(int a,int b,int p){
	map<int,int> hash;hash.clear();
	b%=p;
	int t=(int)sqrt(p)+1;
	for(int j=0;j<t;j++){
		int val=(long long)b*ksm(a,j,p)%p;
		hash[val]=j;
	}
	a=ksm(a,t,p);
	if(a==0) return b==0?1:-1;
	for(int i=0;i<=t;i++){
		int val=ksm(a,i,p);
		int j=hash.find(val)==hash.end()?-1:hash[val];
		if(j>=0 && i*t-j>=0) return i*t-j;
	}
	return -1;
}
signed main(){
	cin>>p>>a>>b;
	int ans=BSGS(a,b,p);
	if(ans==-1) cout<<"no solution";
	else cout<<ans;
}

扩展BSGS

给定 a,p,ba,p,ba,p,b,求满足 ax≡b(modp)a^x≡b \pmod pax≡b(modp) 的最小自然数 xxx

\(\mathfrak{talk\; is\; easy\; show\; me\; the\; code}\)

#include<bits/stdc++.h>
#define int long long
using namespace std;
int a,b,p;
map<int,int>hash;
inline int gcd(int a,int b){return b==0?a:gcd(b,a%b);}
inline void exgcd(int a,int b,int &x,int &y){
	if(b==0)x=1,y=0;
	else{exgcd(b,a%b,y,x);y-=a/b*x;}
}
inline int inv(int a,int b){
	int x,y;
	exgcd(a,b,x,y);
	return (x%b+b)%b;
}
inline int ksm(int a,int b,int p){
	int ans=1;
	while(b){
		if(b&1) ans=ans*a%p;
		a=a*a%p;
		b>>=1;
	}
	return ans;
}
inline int BSGS(int a,int b,int p){
	hash.clear();
	int k=ceil(sqrt(p));
	b%=p;
	for(int i=1;i<=k;i++){b=b*a%p,hash[b]=i;}
	int tmp=ksm(a,k,p);b=1;
	for(int i=1;i<=k;i++){
		b=b*tmp%p;
		if(hash.find(b)!=hash.end()) return (i*k-hash[b]+p)%p;
	}
	return -1;
}
inline int EXBSGS(int a,int b,int p){
	if(b==1||p==1) return 0;
	int GCD=gcd(a,p),k=0,q=1;
	while(GCD>1){
		if(b%GCD!=0) return -1;
		k++;b/=GCD,p/=GCD;q=q*(a/GCD)%p;
		if(q==b) return k;
		GCD=gcd(a,p);
	}
	int flag=BSGS(a,b*inv(q,p)%p,p);
	if(flag==-1) return -1;
	return flag+k;
}

namespace Read{
    template<typename T>
    inline void read(T &x){
        x=0;T f=1;char ch=getchar();
        while (!isdigit(ch)) {if(ch=='-')f=-1;ch=getchar();}
        while (isdigit(ch)){x=x*10+(ch^48);ch=getchar();}
        x*=f;
    }

    template <typename T, typename... Args>
    inline void read(T& t, Args&... args) {
        read(t); read(args...);
    }
}

using namespace Read;

signed main(){
	read(a,p,b);
	while(a || b || p){
		a%=p,b%=p;
		int ans=EXBSGS(a,b,p);
		if(ans==-1) puts("No Solution");
		else printf("%d\n",ans);
		read(a,p,b);
	}
	return 0;
}

乘法逆元

\(\mathfrak{talk\; is\; easy\; show\; me\; the\; code}\)

#include<bits/stdc++.h>
using namespace std;
#define ll long long
const int maxn=3e6+5;
ll inv[maxn];
int main(){
	int n,p;
	scanf("%d%d",&n,&p);
	cout<<"1"<<endl;
	inv[1]=1;
	for(int i=2;i<=n;i++){
		inv[i]=(ll)p-(p/i)*inv[p%i]%p;
		printf("%d\n",inv[i]);
	}
	return 0;
}

\(\mathfrak{talk\; is\; easy\; show\; me\; the\; code}\)

#include<bits/stdc++.h>
using namespace std;
#define ll long long
const int maxn=5e6;
#define re register
inline int read(){
	int x=0,f=1;char ch=getchar();
	while (!isdigit(ch)){if (ch=='-') f=-1;ch=getchar();}
	while (isdigit(ch)){x=x*10+ch-48;ch=getchar();}
	return x*f;
}
int n,k,mod,inv[maxn],a[maxn],s[maxn];
inline int fpow(int a,int b,int p){
	int ans=1;
	while(b){
		if(b&1)ans=(ll)ans*a%p;
		a=(ll)a*a%p;
		b>>=1;
	}	
	return ans%p;
}
int main(){
	n=read();
	mod=read();
	k=read();
	int ans=0;
	memset(s,1,sizeof s);		
	for(re int i=1;i<=n;++i){
		a[i]=read();
		s[i]=(ll)s[i-1]*a[i]%mod;
	}
	
	inv[n]=fpow(s[n],mod-2,mod);	
	for(re int i=n-1;i>0;--i)		inv[i]=(ll)inv[i+1]*a[i+1]%mod;
	for(re int i=n;i>0;--i)	ans=((ll)inv[i]*s[i-1]%mod+ans)*k %mod;			
	printf("%d\n",ans);		
	return 0;
} 

\(\mathfrak{talk\; is\; easy\; show\; me\; the\; code}\)



矩阵

矩阵乘法

矩阵快速幂

\(\mathfrak{talk\; is\; easy\; show\; me\; the\; code}\)

#include<bits/stdc++.h>
#define int long long
using namespace std;
const int N=205,mod=1e9+7;
int n,k,a[N][N],b[N][N],c[N][N];
inline void c1(){//自乘 
	for(int i=1;i<=n;i++)for(int j=1;j<=n;j++)for(int k=1;k<=n;k++)b[i][j]=(b[i][j]+a[i][k]*a[k][j])%mod;
	for(int i=1;i<=n;i++)for(int j=1;j<=n;j++)a[i][j]=b[i][j];			
}
inline void c2(){//答案乘 
	for(int i=1;i<=n;i++)for(int j=1;j<=n;j++)for(int k=1;k<=n;k++)b[i][j]=(b[i][j]+c[i][k]*a[k][j])%mod;
	for(int i=1;i<=n;i++)for(int j=1;j<=n;j++)c[i][j]=b[i][j];
}
void ksm(int k){
	for(int i=1;i<=n;i++)c[i][i]=1;//单位矩阵,等价于:int ans=1; 
	while(k){if(k&1){memset(b,0,sizeof(b));c2();}k>>=1;memset(b,0,sizeof(b));c1();}
}
inline int read(){int x=0,f=1;char ch=getchar();while(!isdigit(ch)){if(ch=='-')f=-1;ch=getchar();}while(isdigit(ch)){x=(x<<3)+(x<<1)+ch-'0';ch=getchar();}return x*f;}
signed main(){
	n=read();k=read();
	for(int i=1;i<=n;i++)for(int j=1;j<=n;j++)a[i][j]=read();
	ksm(k);				
	for(int i=1;i<=n;i++){for(int j=1;j<=n;j++)printf("%lld ",c[i][j]);printf("\n");}
	return 0;	 
}

\(\mathfrak{talk\; is\; easy\; show\; me\; the\; code}\)

#include<bits/stdc++.h>
#define ll long long
using namespace std;
const int mod=1e9+7;
ll n,k;
struct matrix{
    ll A[115][115];
    inline void init(){memset(A,0,sizeof(A));}
    inline ll* operator [] (const int k){return A[k];}
    inline matrix operator *(matrix &B){
        matrix res;res.init();
        for(int i=0;i<n;i++)for(int j=0;j<n;j++)for(int t=0;t<n;t++)
            res[i][j]=(res[i][j]+A[i][t]*B[t][j])%mod;
        return res;
    }
    inline matrix ksm(ll x){
        matrix res;res.init();matrix tmp=*this;      
        for(int i=0;i<n;i++) res[i][i]=1;
        while(x){
            if(x&1) res=res*tmp;
            tmp=tmp*tmp;x>>=1;          
        }
        return res;
    }
}MAP;
int main(){
    scanf("%lld%lld",&n,&k);
    for(int i=0;i<n;i++)for(int j=0;j<n;j++)scanf("%lld",&MAP[i][j]);
    MAP=MAP.ksm(k);
    for(int i=0;i<n;i++){
        for(int j=0;j<n;j++) printf("%lld ",MAP[i][j]);
        putchar('\n');
    }
    return 0;
}

矩阵加速

\(\mathfrak{talk\; is\; easy\; show\; me\; the\; code}\)

#include<bits/stdc++.h>
#define int long long
using namespace std;
const int N=205;
const int mod=1e9+7;
int n,k,a[N][N],b[N][N],c[N][N];
inline void c1(){
	memset(b,0,sizeof(b));
	for(int i=1;i<=n;i++)
		for(int j=1;j<=n;j++)
			for(int k=1;k<=n;k++)
				b[i][j]=(b[i][j]+a[i][k]*a[k][j])%mod;
	for(int i=1;i<=n;i++)
		for(int j=1;j<=n;j++)
			a[i][j]=b[i][j];			
}
inline void c2(){
	memset(b,0,sizeof(b));
	for(int i=1;i<=n;i++)
		for(int j=1;j<=n;j++)
			for(int k=1;k<=n;k++)
				b[i][j]=(b[i][j]+c[i][k]*a[k][j])%mod;
	for(int i=1;i<=n;i++)
		for(int j=1;j<=n;j++)
			c[i][j]=b[i][j];
}
void ksm(int b){
	for(int i=1;i<=n;i++)c[i][i]=1; 
	while(b){
		if(b&1)c2();
		b>>=1;c1();
	}
}
inline int read(){
	int x=0,f=1;char ch=getchar();
	while(!isdigit(ch)){if(ch=='-')f=-1;ch=getchar();}
	while(isdigit(ch)){x=(x<<3)+(x<<1)+ch-'0';ch=getchar();}
	return x*f;
}
signed main(){
	k=read();n=2;
	if(k==1 || k==2){printf("1");return 0;}
	a[1][1]=1;a[1][2]=1;a[2][1]=1;a[2][2]=0;
	int f[1][2];f[1][1]=1;f[1][2]=1;
	ksm(k-2);
	memset(b,0,sizeof(b));				
	for(int j=1;j<=2;j++)
		for(int k=1;k<=2;k++)
			b[1][j]=(b[1][j]+f[1][k]*c[k][j])%mod;
	printf("%lld",b[1][1]);
	return 0;	 
}

高斯消元

给定一个线性方程组,对其求解

\(\mathfrak{talk\; is\; easy\; show\; me\; the\; code}\)

#include <cmath>
#include <cstdio>
#include <algorithm>
using namespace std;
int n = 0;
double k[101][101], b[101];
bool Gauss()
{
	for (int i = 1; i <= n; ++i)
	{
		int ex = i;
		for (int j = i + 1; j <= n; ++j)
			if (fabs(k[j][i]) > fabs(k[ex][i]))
				ex = j;
		if (fabs(k[ex][i]) < 1e-8)
			return false;
		if (ex != i) {
			for (int p = 1; p <= n; ++p)
				swap(k[i][p], k[ex][p]);
			swap(b[i], b[ex]);
		}
		for (int j = 1; j <= n; ++j)
		{
			if (j == i || fabs(k[j][i]) < 1e-8)
				continue;
			double c = k[j][i] / k[i][i];
			for (int p = 1; p <= n; ++p)
				k[j][p] -= k[i][p] * c;
			b[j] -= b[i] * c;
		}
	}
	return true;
}
int main()
{
	scanf("%d", &n);
	for (int i = 1; i <= n; ++i) {
		for (int j = 1; j <= n; ++j)
			scanf("%lf", &k[i][j]);
		scanf("%lf", &b[i]);
	}
	if(Gauss())
		for (int i = 1; i <= n;++i)
			printf("%.2lf\n", b[i] / k[i][i]);
	else
		printf("No Solution");
	return 0;
}

组合数


数据结构


并查集

路径压缩

扩展域和边带权


ST表

正写ST表

\(\mathfrak{talk\; is\; easy\; show\; me\; the\; code}\)

#include<bits/stdc++.h>
using namespace std;
int n,m,s[100005][25];
int query(int l,int r){
    int k=log2(r-l+1);
    return max(s[l][k],s[r-(1<<k)+1][k]);
}
inline int read(){
    int x=0,f=1;char ch=getchar();
    while(!isdigit(ch)){if(ch=='-')f=-1;ch=getchar();}
    while(isdigit(ch)){x=(x<<3)+(x<<1)+ch-'0';ch=getchar();}
    return x*f;
}
void work(int n){
    for(int i=1;i<=n;i++)s[i][0]=read();
    for(int j=1;j<=19;j++)
        for(int i=1;i+(1<<j)-1<=n;i++)
            s[i][j]=max(s[i][j-1],s[i+(1<<(j-1))][j-1]);
}
int main(){
    n=read();m=read();
    work(n);
    
    while(m--){
        int l=read(),r=read();
        printf("%d\n",query(l,r));
    }
    return 0;
}

反写ST表


树状数组

基本操作

二分与倍增

二维树状数组


分块

分块初步

求众数

\(\mathfrak{talk\; is\; easy\; show\; me\; the\; code}\)

#include<bits/stdc++.h>
using namespace std;
const int N=1e6+105;
int n,m,block;
int a[N],b[N],f[N],tot;
int d[1000][1000];
vector<int> g[N];
int ct[N];
inline void build(){
    block=max(1,(int)(n/sqrt(m*log2(n))));
    for(int i=1;i<=n;i++)b[i]=(i-1)/block+1;
}
inline int ef(int l,int r,int val){
    int t=upper_bound(g[val].begin(),g[val].end(),r)-lower_bound(g[val].begin(),g[val].end(),l);
    return t;
}
inline void pre(int x){
    memset(ct,0,sizeof(ct));
    int mx=-1,ans=0;
    for (int i =(x-1)*block+1;i<=n;i++){
        ct[a[i]]++;
        if (ct[a[i]]>mx||(ct[a[i]]==mx&&a[i]<ans)){ans=a[i];mx=ct[a[i]];}        
        d[x][b[i]]=ans;
    }
}
inline int query(int l,int r){
    int ans=d[b[l]+1][b[r]-1];
    int mx=ef(l,r,ans);
    int cnt=0;
    int up=min(r,b[l]*block);
    for(int i=l;i<=up;i++){
        cnt=ef(l, r, a[i]);
        if(cnt>mx||(cnt==mx&&f[ans]>f[a[i]])){mx=cnt;ans=a[i];}
    }
    if(b[l]!=b[r]){
        for(int i=(b[r]-1)*block+1;i<=r;i++){
            cnt=ef(l,r,a[i]);
            if(cnt>mx||(cnt==mx&&ans>a[i])){mx=cnt;ans=a[i];}
        }
    }
    return ans;
}
inline int read(){
	int x=0,f=1;char ch=getchar();
	while(!isdigit(ch)){if(ch=='-')f=-1;ch=getchar();}
	while(isdigit(ch)){x=(x<<3)+(x<<1)+ch-'0';ch=getchar();}
	return x*f;
}
int main(){
    n=read();m=read();
    for (int i=1;i<=n;i++){a[i]=read();f[i]=a[i];}
    build();
    sort(f+1,f+n+1);
    int N=unique(f+1,f+n+1)-f-1;
    for (int i=1;i<=n;i++){
        a[i]=lower_bound(f+1,f+N+1,a[i])-f;
        g[a[i]].push_back(i);
    }
    int ans=0;
    for(int i=1;i<=b[n];i++)pre(i);
    while (m--){
        int l,r;
        l=read();r=read();
        l=(l+ans-1)%n+1;r=(r+ans-1)%n+1;
        if(l>r)swap(l,r);
        ans=f[query(l,r)];
        printf("%d\n",ans);
    }
    return 0;
}

\(\mathfrak{talk\; is\; easy\; show\; me\; the\; code}\)

//    1 x y k:将区间 [x,y] 内每个数加上 k。
//    2 x y:输出区间 [x,y] 内每个数的和。

#include<bits/stdc++.h>
#define int long long
using namespace std;
const int N=1e6+105;
int n,m,block,ed[N],st[N],pos[N],t;
int a[N],add[N],sum[N];
void pre(){
    block=sqrt(n);
    t=n/block;
    if(n%block) t++;
    for(int i=1;i<=t;i++){
        st[i]=(i-1)*block+1;
        ed[i]=i*block;
    }
    ed[t]=n;
    for(int i=1;i<=n;i++)pos[i]=(i-1)/block+1;
    for(int i=1;i<=t;i++)for(int j=st[i];j<=ed[i];j++) sum[i]+=a[j];
}

void change(int L,int R,int d){
    int p=pos[L],q=pos[R];
    if(p==q){
        for(int i=L;i<=R;i++) a[i]+=d;
        sum[p]+=d*(R-L+1);
    }
    else{
        for(int i=p+1;i<=q-1;i++) add[i]+=d;
        for(int i=L;i<=ed[p];i++) a[i]+=d;
        sum[p]+=d*(ed[p]-L+1);
        for(int i=st[q];i<=R;i++) a[i]+=d;
        sum[q]+=d*(R-st[q]+1);
    }
}
long long ask(int L,int R){
    int p=pos[L],q=pos[R];
    long long ans=0;
    if(p==q){
        for(int i=L;i<=R;i++) ans+=a[i];
        ans+=add[p]*(R-L+1);
    }
    else{
        for(int i=p+1;i<=q-1;i++) ans+=sum[i]+add[i]*(ed[i]-st[i]+1);
        for(int i=L;i<=ed[p];i++) ans+=a[i];
        ans+=add[p]*(ed[p]-L+1);
        for(int i=st[q];i<=R;i++) ans+=a[i];
        ans+=add[q]*(R-st[q]+1);
    }
    return ans;
}
inline int read(){
    int x=0,f=1;char ch=getchar();
    while(!isdigit(ch)){if(ch=='-')f=-1;ch=getchar();}
    while(isdigit(ch)){x=(x<<3)+(x<<1)+ch-'0';ch=getchar();}
    return x*f;
}

signed main(){
    n=read();m=read();
    for(int i=1;i<=n;i++)a[i]=read();
    pre();
    for(int i=1;i<=m;i++){
        int opt,x,y,z;
        opt=read();
        if(opt==1){
            x=read();y=read();z=read();
            change(x,y,z);
        }
        if(opt==2){
            x=read();y=read();
            long long ans=ask(x,y);
            printf("%lld\n",ans);
        }
    }
    return 0;
}

线段树

线段树初步

\(\mathfrak{talk\; is\; easy\; show\; me\; the\; code}\)

#include<bits/stdc++.h>
using namespace std;
#define ll long long
const int maxn=1e6+5;
unsigned ll n,m,a[maxn],ans[maxn<<2],tag[maxn<<2];
inline ll ls(int p){return p<<1;}
inline ll rs(ll p){return p<<1|1;}
void scan(){
	cin>>n>>m;
	for(ll i=1;i<=n;i++){
		scanf("%lld",&a[i]);
	}
}
inline void push_up(ll p){
	ans[p]=ans[ls(p)]+ans[rs(p)];
}
void build(ll p,ll l,ll r){
	tag[p]=0;
	if(l==r){
		ans[p]=a[l];
		return ;
	}
	ll mid=(l+r)>>1;
	build(ls(p),l,mid);
	build(rs(p),mid+1,r);
	push_up(p);
}
inline void f(ll p,ll l,ll r,ll k){
	tag[p]=tag[p]+k;
	ans[p]=ans[p]+k*(r-l+1);
}
inline void push_down(ll p,ll l,ll r){
	ll mid=(l+r)>>1;
	f(ls(p),l,mid,tag[p]);
	f(rs(p),mid+1,r,tag[p]);
	tag[p]=0;
}
inline void update(ll n1,ll nr,ll l,ll r,ll p,ll k){
	if(n1<=l&&r<=nr){
		ans[p]+=k*(r-l+1);
		tag[p]+=k;
		return ;
	}
	push_down(p,l,r);
	ll mid=(l+r)>>1;
	if(n1<=mid)update(n1,nr,l,mid,ls(p),k);
	if(nr>mid)update(n1,nr,mid+1,r,rs(p),k);
	push_up(p);
}
ll query(ll q_x,ll q_y,ll l,ll r,ll p){
	ll res=0;
	if(q_x<=l&&r<=q_y){
		return ans[p];
	}
	ll mid=(l+r)>>1;
	push_down(p,l,r);
	if(q_x<=mid)res+=query(q_x,q_y,l,mid,ls(p));
	if(q_y>mid)res+=query(q_x,q_y,mid+1,r,rs(p));
	return res;
}
int main(){
	ll a1,b,c,d,e,f;
	scan();
	build(1,1,n);
	while(m--){
		scanf("%lld",&a1);
		switch(a1){
			case 1:{
				scanf("%lld%lld%lld",&b,&c,&d);
				update(b,c,1,n,1,d);
				break;
			}
			case 2:{
				scanf("%lld%lld",&e,&f);
				printf("%lld\n",query(e,f,1,n,1));
				break;
			}
		}
	}
	return 0;
}

平衡树

Treap

FHQ Treap

\(\mathfrak{talk\; is\; easy\; show\; me\; the\; code}\)

//重构次数:2 
//题目:FHQ Treap 
//不压行 
#include<bits/stdc++.h> 
using namespace std;
const int N=1e5+105;
int son[N][3],val[N],ran[N],siz[N],SIZE,n,root,x,y,z,opt,a;
void Up(int x){siz[x]=1+siz[son[x][0]]+siz[son[x][1]];} 
int New(int v){
	siz[++SIZE]=1;
	val[SIZE]=v;ran[SIZE]=rand();
	return SIZE;
}
int merge(int x,int y){
	if(!x||!y) return x+y;
	if(ran[x]<ran[y]){
		son[x][1]=merge(son[x][1],y);
		Up(x);return x;
	}
	else {
		son[y][0]=merge(x,son[y][0]);
		Up(y);return y;
	}
}
void split(int p,int k,int &x,int &y){
	if(!p) x=y=0;
	else{
		if(val[p]<=k) x=p,split(son[p][1],k,son[p][1],y);
		else y=p,split(son[p][0],k,x,son[p][0]);
		Up(p);
	}
}
int kth(int p,int k){
	while(1){
		if(k<=siz[son[p][0]])p=son[p][0];
		else if(k==siz[son[p][0]]+1) return p;
		else k-=siz[son[p][0]]+1,p=son[p][1];
	}
}
int main(){
	srand(1e9+7);
	scanf("%d",&n);
	for(int i=1;i<=n;i++){
		scanf("%d%d",&opt,&a);
		if(opt==1){//插入 
			split(root,a,x,y);
			root=merge(merge(x,New(a)),y);
		}
		else if(opt==2){
			split(root,a,x,z);split(x,a-1,x,y);
			y=merge(son[y][0],son[y][1]);root=merge(merge(x,y),z);
		}
		else if(opt==3){
			split(root,a-1,x,y);
			printf("%d\n",siz[x]+1);
			root=merge(x,y);
		}
		else if(opt==4){printf("%d\n",val[kth(root,a)]);}
		else if(opt==5){
			split(root,a-1,x,y);
			printf("%d\n",val[kth(x,siz[x])]);
			root=merge(x,y);
		}
		else if(opt==6){
			split(root,a,x,y);
			printf("%d\n",val[kth(y,1)]);
			root=merge(x,y);
		}
	}
	return 0;
}

文艺平衡树

\(\mathfrak{talk\; is\; easy\; show\; me\; the\; code}\)

//重构次数:1
//不压行 
#include<bits/stdc++.h> 
using namespace std;
const int N=1e5+105;
int son[N][3],val[N],ran[N],siz[N],SIZE,n,root,m;
bool f[N];
void Up(int x){siz[x]=1+siz[son[x][0]]+siz[son[x][1]];} 
void Down(int x){
	swap(son[x][0],son[x][1]);
	if(son[x][0])f[son[x][0]]^=1;
	if(son[x][1])f[son[x][1]]^=1;
	f[x]=0;
}
int New(int v){
	siz[++SIZE]=1;
	val[SIZE]=v;ran[SIZE]=rand();
	return SIZE;
}
int merge(int x,int y){
	if(!x||!y) return x+y;
	if(ran[x]<ran[y]){
		if(f[x])Down(x);
		son[x][1]=merge(son[x][1],y);
		Up(x);return x;
	}
	else {
		if(f[y])Down(y);
		son[y][0]=merge(x,son[y][0]);
		Up(y);return y;
	}
}
void split(int p,int k,int &x,int &y){
	if(!p) x=y=0;
	else{
		if(f[p])Down(p);//下面用siz,而不是val 
		if(siz[son[p][0]]<k) x=p,split(son[p][1],k-siz[son[p][0]]-1,son[p][1],y);
		else y=p,split(son[p][0],k,x,son[p][0]);
		Up(p);
	}
}
/*int kth(int p,int k){
	while(1){
		if(k<=siz[son[p][0]])p=son[p][0];
		else if(k==siz[son[p][0]]+1) return p;
		else k-=siz[son[p][0]]+1,p=son[p][1];
	}
}*/
void out(int x){
	if(!x) return ;
	if(f[x])Down(x);
	out(son[x][0]);
	printf("%d ",val[x]) ;
	out(son[x][1]);
}

int main(){
	scanf("%d%d",&n,&m);
	for(int i=1;i<=n;i++)root=merge(root,New(i));
	for(int i=1;i<=m;i++){
		int l,r,x,y,z;
		scanf("%d%d",&l,&r);
		split(root,l-1,x,y);split(y,r-l+1,y,z);
		f[y]^=1;root=merge(x,merge(y,z));
	}
	out(root);
	return 0;
}

主席树

初步

如题,你需要维护这样的一个长度为 N N N 的数组,支持如下几种操作

在某个历史版本上修改某一个位置上的值

访问某个历史版本上的某一位置的值

\(\mathfrak{talk\; is\; easy\; show\; me\; the\; code}\)

#include<bits/stdc++.h>
using namespace std;

const int N=1e6+5;
inline int read(){
    int f=1,x=0;char ch;
    do{ch=getchar();if(ch=='-')f=-1;}while(ch<'0'||ch>'9');
    do{x=x*10+ch-'0';ch=getchar();}while(ch>='0'&&ch<='9');
    return f*x;
}
int n,m,top=0,a[N],root[N<<5];
struct hjttree{
	int l,r,val;
}tree[N<<5];
inline int clone(int node){
	top++;
	tree[top]=tree[node];
	return top;
}//н¨½Úµã 
inline int build(int node,int l,int r){
	node=++top;
	if(l==r){
		tree[node].val=a[l];
		return top;
	}
	int mid=(l+r)>>1;
	tree[node].l=build(tree[node].l,l,mid);
	tree[node].r=build(tree[node].r,mid+1,r);
	return node;
}
int update(int node,int l,int r,int x,int val){
	node=clone(node);//¸üоÍҪн¨½Úµã
	if(l==r){
		tree[node].val=val;
	} 
	else {
		int mid=(l+r)>>1;
		if(x<=mid){
			tree[node].l=update(tree[node].l,l,mid,x,val);
		}
		else {
			tree[node].r=update(tree[node].r,mid+1,r,x,val);
		}
	}
	return node;
}
int query(int node,int l,int r,int x){
	if(l==r){
		return tree[node].val;
	}
	else {
		int mid=(l+r)>>1;
		if(x<=mid){
			return query(tree[node].l,l,mid,x);
		}
		else {
			return query(tree[node].r,mid+1,r,x);
		}
	}
}
int main(){
	n=read(),m=read();
	for(int i=1;i<=n;i++)cin>>a[i];
	root[0]=build(0,1,n);
	for(int i=1,opt,mode,x,y;i<=m;i++){
		opt=read(),mode=read(),x=read();
		if(mode==1){
			y=read();
			root[i]=update(root[opt],1,n,x,y);
		}
		else {
			cout<<query(root[opt],1,n,x)<<endl;
			root[i]=root[opt];
		}
	}
	return 0;
}

\(\mathfrak{talk\; is\; easy\; show\; me\; the\; code}\)

//主席树
//给定n个数组成的序列a,将对于指定闭区间[l,r]查询其区间内的第k小值
#include<bits/stdc++.h>
using namespace std;
const int N=2e5+5;
int lc[N<<5],rc[N<<5],rt[N],sum[N<<5],a[N],b[N],cnt,n,m,p,ql,qr,l,r,k,q,ans;
inline void build(int &x,int l,int r){
	int oo=++cnt;
	if(l==r)return ;
	build(lc[oo],l,((l+r)>>1));build(rc[oo],((l+r)>>1)+1,r);
}
int modify(int o,int l,int r){
	int oo=++cnt;
	lc[oo]=lc[o];rc[oo]=rc[o];sum[oo]=sum[o]+1;
	if(l==r){return oo;}
	if(p<=((l+r)>>1))lc[oo]=modify(lc[oo],l,((l+r)>>1));
	else rc[oo]=modify(rc[oo],((l+r)>>1)+1,r);
	return oo;
}
int query(int u,int v,int l,int r,int k){
	int ans,mid=(l+r)>>1,x=sum[lc[v]]-sum[lc[u]];
	if(l==r)return l;
	if(x>=k)ans=query(lc[u],lc[v],l,mid,k);
	else ans=query(rc[u],rc[v],mid+1,r,k-x);
	return ans;
}
int main(){
	cin>>n>>m;
	for(int i=1;i<=n;i++){cin>>a[i];b[i]=a[i];}
	sort(b+1,b+1+n);q=unique(b+1,b+n+1)-b-1;build(rt[0],1,q);
	for(int i=1;i<=n;i++){
		p=lower_bound(b+1,b+q+1,a[i])-b;
		rt[i]=modify(rt[i-1],1,q);}
	while(m--){
		cin>>l>>r>>k;
		ans=query(rt[l-1],rt[r],1,q,k);
		cout<<b[ans]<<endl;}
	return 0;
}

左偏树

如题,一开始有 nnn 个小根堆,每个堆包含且仅包含一个数。接下来需要支持两种操作:

1 x y:将第 xxx 个数和第 yyy 个数所在的小根堆合并(若第 xxx 或第 yyy 个数已经被删除或第 xxx 和第 yyy 个数在用一个堆内,则无视此操作)。

2 x:输出第 xxx 个数所在的堆最小数,并将这个最小数删除(若有多个最小数,优先删除先输入的;若第 xxx 个数已经被删除,则输出 −1-1−1 并无视删除操作)。

\(\mathfrak{talk\; is\; easy\; show\; me\; the\; code}\)

#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std;
template <typename T>
inline T read(){
	T x=0;char ch=getchar();bool fl=false;
	while(!isdigit(ch)){if(ch=='-')fl=true;ch=getchar();}
	while(isdigit(ch)){
		x=(x<<3)+(x<<1)+(ch^48);ch=getchar();
	}
	return fl?-x:x;
}
const int maxn = 1e5 + 10;
int n,m,val[maxn],dis[maxn],fa[maxn];
int ls[maxn],rs[maxn];
int merge(int x,int y){
	if(!x || !y)return x|y;
	if(val[x]>val[y])swap(x,y);
	rs[x]=merge(rs[x],y);
	fa[rs[x]]=x;
	if(dis[ls[x]]<dis[rs[x]])swap(ls[x],rs[x]);
	dis[x]=dis[rs[x]]+1;
	return x;
}
inline int find(int x){
	while(x!=fa[x])x=fa[x]=fa[fa[x]];
	return x;
}
#define read() read<int>()
int main(){
	n=read();m=read();
	for(int i=1;i<=n;i++)val[i]=read();
	for(int i=1;i<=n;i++)fa[i]=i;
	while(m--){
		int op=read();
		if(op==1){
			int x=read(),y=read();
			if(!val[x] || !val[y])continue;
			x=find(x);y=find(y);
			if(x!=y)fa[x]=fa[y]=merge(x,y);
		}
		else {
			int x=read();
			if(!val[x]){
				puts("-1");continue;
			}
			x=find(x);
			printf("%d\n",val[x]);
			val[x]=0;
			fa[ls[x]]=ls[x];fa[rs[x]]=rs[x];
			fa[x]=merge(ls[x],rs[x]);
		}
	}
	return 0;
}


点分治

树的重心

点分治


动态规划

初步

单调队列优化

斜率优化

数位dp

区间dp

背包

树上问题


字符串


哈希

一维哈希

自然溢出和大模数

双哈希

二维哈希

k叉树

荷马史诗

\(\mathfrak{talk\; is\; easy\; show\; me\; the\; code}\)

#include"bits/stdc++.h"
using namespace std;
#define ll long long
struct node{
	ll w,h;
	node(ll w,ll h):w(w),h(h){}
	bool operator < (const node &a)const{
		return w==a.w?h>a.h:w>a.w;
	}
};
ll ans;
priority_queue <node> q;
int main(){
	ios::sync_with_stdio(false);
	ll n,k,ans=0;
	cin>>n>>k;
	for(int i=1;i<=n;i++){
		ll w;
		cin>>w;
		q.push((node){w,1});
	}
	while((q.size()-1)%(k-1)!=0)q.push((node){0,1});
	while(q.size()>=k){
		ll h=-1,w=0;
		for(int i=1;i<=k;i++){
			node t=q.top();
			q.pop();
			h=max(h,t.h);
			w+=t.w;
		}
		ans+=w;
		q.push((node){w,h+1});
	}
	cout<<ans<<endl<<q.top().h-1<<endl;
	
	return 0;
}

KMP算法

\(\mathfrak{talk\; is\; easy\; show\; me\; the\; code}\)

#include<bits/stdc++.h>
using namespace std;
const int N=1e6+105;
int la,lb,nex[N],f[N];
char a[N],b[N];
int main(){
    cin>>(a+1);cin>>(b+1);
    la=strlen(a+1);lb=strlen(b+1);f[1]=0;
    for(int i=2,j=0;i<=lb;i++){
        while(j>0&&b[i]!=b[j+1])j=nex[j];
        if(b[i]==b[j+1])j++;
        nex[i]=j;    
    }
    for(int i=1,j=0;i<=la;i++){
        while(j>0&&(j==lb||a[i]!=b[j+1]))j=nex[j];
        if(a[i]==b[j+1])j++;
        f[i]=j;
        if(f[i]==lb){printf("%d\n",i-lb+1);}
    }
    for(int i=1;i<=lb;i++)printf("%d ",nex[i]);
    return 0;
}

manachaer

\(\mathfrak{talk\; is\; easy\; show\; me\; the\; code}\)

#include<bits/stdc++.h>
using namespace std;
const int N=11000005;
char s[N],s_new[N<<1];
int p[N<<1];
inline int init(){
    int len=strlen(s);
    s_new[0]='$';s_new[1]='#';
    int j=2;
    for(int i=0;i<len;i++){s_new[j++]=s[i];s_new[j++]='#';}
    s_new[j]='\0';
    return j;
}
int manacher(){
    int len=init(),max_len=-1,id,mx=0;
    for(int i=1;i<=len;i++){
        if(i<mx)p[i]=min(p[2*id-i],mx-i);
        else p[i]=1;
        while(s_new[i-p[i]]==s_new[i+p[i]])p[i]++;
        if(mx<i+p[i]){id=i;mx=i+p[i];}
        max_len=max(max_len,p[i]-1);
    }
    return max_len;
}
int main(){
    cin>>s;
    printf("%d",manacher());
    return 0;
}

trie树

01 trie

26 trie

AC自动机

有 NNN 个由小写字母组成的模式串以及一个文本串 TTT。每个模式串可能会在文本串中出现多次。你需要找出哪些模式串在文本串 TTT 中出现的次数最多。

\(\mathfrak{talk\; is\; easy\; show\; me\; the\; code}\)

#include<bits/stdc++.h>
using namespace std;
const int N=1e6+105;
struct trie{int fail,v[26],end;}t[N];
struct result{
	int num,pos;
	bool operator <(const result &other)const{return num==other.num?pos<other.pos:num>other.num;}
}ans[N];
int cnt,n;
string s[155];
inline void build(string s,int num){
	int l=s.length(),now=0;
	for(int i=0;i<l;i++){
		if(t[now].v[s[i]-'a']==0)t[now].v[s[i]-'a']=++cnt;
		now=t[now].v[s[i]-'a'];
	}
	t[now].end=num;
}
void GF(){
	queue<int> q;
	for(int i=0;i<26;i++)
		if(t[0].v[i]!=0){t[t[0].v[i]].fail=0;q.push(t[0].v[i]);}
	while(!q.empty()){
		int u=q.front();q.pop();
		for(int i=0;i<26;i++)
			if(t[u].v[i]!=0){t[t[u].v[i]].fail=t[t[u].fail].v[i];q.push(t[u].v[i]);}
			else t[u].v[i]=t[t[u].fail].v[i];
	}
}
void AC(string s){
	int l=s.length(),now=0;
	for(int i=0;i<l;i++){
		now=t[now].v[s[i]-'a'];
		for(int j=now;j;j=t[j].fail)ans[t[j].end].num++;
	}
}
int main(){
	while(cin>>n){
		if(!n) break;
		cnt=0;memset(t,0,sizeof(t));
		for(int i=1;i<=n;i++){
			cin>>s[i];
			ans[i].num=0;ans[i].pos=i;
			build(s[i],i);
		}
		t[0].fail=0;GF();
		cin>>s[0];
		AC(s[0]);sort(ans+1,ans+1+n);
		cout<<ans[1].num<<endl<<s[ans[1].pos]<<endl;
		for(int i=2;i<=n;i++)
		if(ans[i].num==ans[i-1].num)cout<<s[ans[i].pos]<<endl;
		else break;
	}
	return 0;
}

给定 nnn 个模式串 sis_isi​ 和一个文本串 ttt,求有多少个不同的模式串在文本串里出现过。
两个模式串不同当且仅当他们编号不同。

\(\mathfrak{talk\; is\; easy\; show\; me\; the\; code}\)

#include<bits/stdc++.h>
using namespace std;
const int N=1e6+105;
int n,T,cnt;
string s;
struct trie{int f,v[26],end;}t[N];
inline void build(string s){
    int l=s.length(),now=0;
    for(int i=0;i<l;i++){
        if(t[now].v[s[i]-'a']==0)t[now].v[s[i]-'a']=++cnt;
        now=t[now].v[s[i]-'a'];
    }
    t[now].end+=1;
}
void GF(){
    queue<int>q;
    for(int i=0;i<26;i++)
        if(t[0].v[i]!=0){t[t[0].v[i]].f=0;q.push(t[0].v[i]);}
    while(!q.empty()){
        int u=q.front();q.pop();
        for(int i=0;i<26;i++)
            if(t[u].v[i]!=0){t[t[u].v[i]].f=t[t[u].f].v[i];q.push(t[u].v[i]);}
            else t[u].v[i]=t[t[u].f].v[i];
    }
}
int AC(string s){
    int l=s.length(),now=0,ans=0;
    for(int i=0;i<l;i++){
        now=t[now].v[s[i]-'a'];
        for(int j=now;j && t[j].end!=-1;j=t[i].f)
            {ans+=t[j].end;t[j].end=-1;}
    }
    return ans;
}
int main(){
    //cin>>T;
    //while(T--){
        cin>>n;
        for(int i=1;i<=n;i++){cin>>s;build(s);}
        GF();
        cin>>s;
        cout<<AC(s)<<endl;
        memset(t,0,sizeof(t));cnt=0;
    //}
    return 0;
}
posted @ 2021-10-11 14:27  Nickle-NI  阅读(43)  评论(0编辑  收藏  举报