常用模板板板

快速幂

注意:在最后输出ans时要再模一次p,以免被指数为0模数为1的情况卡掉

namespace QUICKPOWER{
	ll quick_power(ll x,ll y,ll md) {
		ll s=1;
		while(y) {
			if(y&1) s=s*x%md;
			y>>=1,x=x*x%md;
		}
		return s%md;
	}
}

逆元、组合数

namespace COMBINATION{
	ll quick_power(ll x,ll y,ll P) {
		ll s=1;
		while(y) {
			if(y&1) s=s*x%P;
			y>>=1,x=x*x%P;
		}
		return s%P;
	}
	ll inv(ll x,ll P) {
		return quick_power(x,P-2,P);
	}
	ll C(ll x,ll y,ll P) {
		ll s=1;
		for(int i=1,j=x;i<=y;i++,j--) {
			s=s*j%P*inv(i,P)%P;
		}
		return s;
	}
}

矩阵快速幂

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

#define maxn 100
#define md (int)(1e9+7)
#define ll long long

int n;
ll K;

struct Node{
    ll a[maxn+5][maxn+5];
    Node(){memset(a,0,sizeof(a));}
    void build() {for(int i=1;i<=n;i++) a[i][i]=1;}
};

Node operator * (const Node& x,const Node& y) {
    Node z;
    for(int k=1;k<=n;k++) {
        for(int i=1;i<=n;i++) {
            for(int j=1;j<=n;j++) {
                z.a[i][j]=(z.a[i][j]+x.a[i][k]*y.a[k][j]%md)%md;
            }
        }
    }
    return z;
}

Node a,ans;

int main() {
    scanf("%d%lld",&n,&K);
    for(int i=1;i<=n;i++) for(int j=1;j<=n;j++) scanf("%lld",&a.a[i][j]);
    
    ans.build();
    do{
        if(K&1) ans=ans*a;
        a=a*a,K>>=1;
    } while(K);
    
    for(int i=1;i<=n;i++) {
        for(int j=1;j<=n;j++) printf("%lld ",ans.a[i][j]);
        printf("\n");
    }
    
    return 0;
}

矩阵优化

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

#define ll long long 
#define md ((int)1e9+7)

struct mtx{
	ll a[5][5];
	mtx operator * (const mtx e) const {
		mtx s;
		memset(s.a,0,sizeof(s.a)); 
		for(int i=1;i<=3;i++) {
			for(int j=1;j<=3;j++) {
				for(int k=1;k<=3;k++) {
					s.a[i][j]=(s.a[i][j]+a[i][k]*e.a[k][j])%md;
				}
			}
		}
		return s;
	}
	void print() {
		for(int i=1;i<=3;i++) {
			for(int j=1;j<=3;j++) {
				printf("%lld ",a[i][j]);
			}
			printf("\n");
		}
	} 
};

int main() {
	
	int T;
	scanf("%d",&T);
	while(T--) {
		int n;
		scanf("%d",&n);
		
		if(n<=3) {
			printf("1\n");
			continue;
		}
		
		mtx ans,x;
		x.a[1][1]=1,x.a[1][2]=0,x.a[1][3]=1;
		x.a[2][1]=1,x.a[2][2]=0,x.a[2][3]=0;
		x.a[3][1]=0,x.a[3][2]=1,x.a[3][3]=0;
		memset(ans.a,0,sizeof(ans.a));
		for(int i=1;i<=3;i++) ans.a[i][i]=1;
		while(n) {
			if(n&1) ans=ans*x;
			x=x*x,n>>=1;
		}
		printf("%lld\n",ans.a[2][1]);
	}
	
	
	return 0; 
} 

线性筛

namespace MAKEPHI{
	const int N=1e7;
	int n,m;
	int Phi[N+5];
	vector<int> prm;

	void makephi(int _n) {
		n=_n;
		Phi[1]=1;
		for(int i=2;i<=n;i++) {
			Phi[i]=i;
		}
		for(int i=2;i<=n;i++) {
			if(Phi[i]==i) Phi[i]--,prm.push_back(i);
			for(int j=0;j<prm.size()&&i*prm[j]<=n;j++) {
				int x=prm[j];
				if(i%x) Phi[i*x]=Phi[i]*x;
				else Phi[i*x]=Phi[i]*Phi[x];
			}
		}
	}
}

数位dp

windy数的实现 (不含前导零且相邻两个数字之差至少为 2 的正整数) :

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

#define read(x) scanf("%d",&x)

int L,R;
int a[20];
int f[20][20][2];

int dp(int x,int fa,int lmt) {
	if(x==0) return 1;
	if(fa!=-1&&f[x][fa][lmt]!=-1) return f[x][fa][lmt];
	int up=9;
	if(lmt) up=a[x];
	int ans=0;
	for(int i=0;i<=up;i++) {
		if(fa!=-1&&abs(i-fa)<2) continue;
		ans+=dp(x-1,(i==0&&fa==-1)?-1:i,lmt&&i==up);
	}
	if(fa!=-1) f[x][fa][lmt]=ans;
	return ans;
}

int slv(int x) {
	memset(f,-1,sizeof(f));
	int len=0;
	while(x) {
		a[++len]=x%10;
		x/=10;
	}
	return dp(len,-1,1);
}

int main() {
	read(L),read(R);
	printf("%d",slv(R)-slv(L-1));
	return 0;
}


kmp

注意:nxt数组的意义——最长公共前后缀长

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

#define maxn 1000000

char a[maxn+5],b[maxn+5];
int nxt[maxn+5];
int n,m;

int main(){
	scanf("%s%s",a+1,b+1);
	n=strlen(a+1),m=strlen(b+1);
	
	for(int i=2;i<=m;i++) {
		int j=nxt[i-1];
		while(j&&b[i]!=b[j+1]) j=nxt[j];
		if(b[j+1]!=b[i]) nxt[i]=0;
		else nxt[i]=j+1;
	}
	
	int j=0;
	for(int i=1;i<=n;i++) {
		while(a[i]!=b[j+1]&&j) j=nxt[j];
		if(a[i]==b[j+1]) j++;
		if(j==m) {
			printf("%d\n",i-m+1);
		}
	}
	
	for(int i=1;i<=m;i++) printf("%d ",nxt[i]);
	
	return 0;
}

AC自动机

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

#define maxn ((int)1e6)
#define S 26


struct ACautomation{
	int ch[maxn+5][S+5],sz;	//trie数 
	int fail[maxn+5];	//fail指针 
	int val[maxn+5];	//以当前节点结尾的模式串有几个 
	
	ACautomation() {	//初始化 
		memset(ch,0,sizeof(ch)),sz=0;
		memset(fail,0,sizeof(fail));
	}
	
	void insert(string x) {	//加入一个模式串 
		int u=0;
		for(int i=0;i<x.size();i++) {
			int y=x[i]-'a';
			if(!ch[u][y]) ch[u][y]=++sz;	//添加一个新节点
			u=ch[u][y];
		}
		++val[u];
	}
	
	void make_fail() {	//建立fail指针 
		queue<int> que;
		for(int i=0;i<S;i++) {
			if(ch[0][i]) que.push(ch[0][i]);
		} 
		while(!que.empty()) {
			int u=que.front();que.pop();
			for(int i=0;i<S;i++) {
				if(ch[u][i]) {
					que.push(ch[u][i]);
					fail[ch[u][i]]=ch[fail[u]][i]; 
				} else ch[u][i]=ch[fail[u]][i];
			}
		} 
	}
	
	int match(string x) {	//匹配 
		int u=0,sum=0;
		for(int i=0;i<x.size();i++) {
			int y=x[i]-'a';
			u=ch[u][y];
			for(int j=u;j&&(~val[j]);j=fail[j]) {
				sum+=val[j];
				val[j]=-1;
			}
		}
		return sum;
	} 
};

ACautomation ac;

int main() {
	
	int n;
	cin>>n;
	
	for(int i=1;i<=n;i++) {
		string x;
		cin>>x;
		ac.insert(x);
	}
	
	ac.make_fail();
	
	string s;
	cin>>s;
	cout<<ac.match(s);
	
	return 0;
}

字符串哈希

注意:
1、可以用 unsigned long long (ull)的自然溢出处理取模问题
2、使用双哈希不容易产生冲突

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

#define maxn 10000
#define maxm 1500
#define ull unsigned long long
#define read(x) scanf("%d",&x)

struct Pair{
	ull x,y;
	Pair(){}
	Pair(ull xx,ull yy) {x=xx,y=yy;}
	bool operator < (const Pair& oth) const {
		return x<oth.x||(x==oth.x&&y<oth.y);
	} 
	bool operator == (const Pair& oth) const {
		return x==oth.x&&y==oth.y;
	}
};

int n;
Pair hsh[maxn+5];

const int md1=13,md2=131;

void make_hash(int x,char* s,int m) {
	ull hsh1=0,hsh2=0;
	for(int i=0;i<m;i++) {
		hsh1=hsh1*md1+s[i]+1;
		hsh2=hsh2*md2+s[i]+1;
	}
	hsh[x]=Pair(hsh1,hsh2);
}

int main() {
	read(n);
	for(int i=1;i<=n;i++) {
		char s[maxm+5];
		scanf("%s",s);
		make_hash(i,s,strlen(s));
	}
	
	sort(hsh+1,hsh+n+1);
	
	int ans=0;
	for(int i=1;i<=n;i++) {
		if(hsh[i]==hsh[i-1]) continue;
		ans++;
	}
	
	printf("%d",ans);
		
	return 0;
}

最小生成树

注意:
1、正向定义小于运算符
2、图不连通的判断方法是求出的最小生成树的边数不等于n-1
3、边集的数组大小要开maxm,开成了maxn就彻底凉了
4、接上,n==m时一定要注意,不要把n、m弄反

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

#define maxn 5000
#define maxm 200000
#define read(x) scanf("%d",&x)

struct Edge{
	int x,y,z;
	Edge(){}
	Edge(int xx,int yy,int zz) {
		x=xx,y=yy,z=zz;
	}
	bool operator < (const Edge& oth) const {
		return z<oth.z;
	}
};

int n,m;
Edge e[maxm+5];
int fa[maxn+5];

int find(int x) {
	if(fa[x]) return fa[x]=find(fa[x]);
	else return x;
}

int kruskal() {
	int cnt=0,s=0;
	for(int i=1;i<=m;i++) {
		int fa1=find(e[i].x),fa2=find(e[i].y);
		if(fa1==fa2) continue;
		fa[fa1]=fa2;
		cnt++;
		s+=e[i].z;
	}
	if(cnt==n-1) return s;
	else return -1;
}

int main() {
	read(n),read(m);
	for(int i=1;i<=m;i++) read(e[i].x),read(e[i].y),read(e[i].z);
	sort(e+1,e+m+1);
	int ans=kruskal();
	if(ans==-1) printf("orz");
	else printf("%d",ans);	
	return 0;
}

最短路:dijkstra

namespace DIJKSTRA{
	typedef long long ll;
	typedef pair<int,ll> pil;
	const int N=2e6;
	const ll inf=0x3f3f3f3f3f3f3f3f;
	
	int n;
	vector<pil> g[N+5];
	ll dist[N+5];
	bool vis[N+5];
	pil fa[N+5];	//记录路径 
	
	struct cmp{
		bool operator () (pil e1,pil e2) {
			return e1.second>e2.second; 
		}
	};
	priority_queue<pil,vector<pil>,cmp> que;
	
	void init(int _n,int st) {
		n=_n;
		for(int i=1;i<=n;i++) g[i].clear(),vis[i]=0,dist[i]=inf;
		dist[st]=0;
	}
	
	void addedge(int u,int v,ll w) {
		g[u].push_back({v,w});
	}
	
	ll dijkstra(int st,int ed) {
		que.push({st,0});
		while(!que.empty()) {
			int x=que.top().first;
			que.pop();
			if(vis[x]) continue;
			else vis[x]=true;
			
			for(pil t:g[x]) {
				int y=t.first;
				ll w=t.second;
				if(dist[y]>dist[x]+w) {
					dist[y]=dist[x]+w;
					que.push({y,dist[y]});
					fa[y]={x,w}; 
				}
			}
		}
		
		return dist[ed];
	}
}

最短路:floyd

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

#define maxn 100
#define maxm 10000
#define read(x) scanf("%d",&x)
#define inf (1<<30)

int n,m;
int a[maxm+5];
int g[maxn+5][maxn+5];
int dist[maxn+5][maxn+5];

void readin() {
	read(n),read(m);
	for(int i=1;i<=m;i++) read(a[i]);
	for(int i=1;i<=n;i++) for(int j=1;j<=n;j++) read(g[i][j]);
}

void floyd() {
	for(int i=1;i<=n;i++) for(int j=1;j<=n;j++) dist[i][j]=g[i][j];
	for(int k=1;k<=n;k++) {
		for(int i=1;i<=n;i++) {
			for(int j=1;j<=n;j++) {
				dist[i][j]=min(dist[i][j],dist[i][k]+dist[k][j]);
			}
		}
	}
}

void print() {
	int ans=0;
	for(int i=2;i<=m;i++) {
		ans+=dist[a[i-1]][a[i]];
	}
	printf("%d",ans);
}

int main() {
	readin();
	floyd();
	print();
	
	return 0;
}

lca倍增

注意:处理到第2^20个祖先就足够过至少1e6的数据范围了,开大了会影响效率

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

#define maxn 500000
#define maxm 500000
#define read(x) scanf("%d",&x)

int n,m,rt;
vector<int> tr[maxn+5];
int anc[maxn+5][30];
int d[maxn+5];

void dfs(int x,int fa) {
	anc[x][0]=fa;
	for(int i=1;i<=20;i++) {
		anc[x][i]=anc[anc[x][i-1]][i-1];
	}
	d[x]=d[fa]+1;
	for(int i=0;i<tr[x].size();i++) {
		int y=tr[x][i];
		if(y==fa) continue;
		dfs(y,x);
	}
}

int findLCA(int x,int y) {
	if(d[x]<d[y]) swap(x,y);
	for(int i=20;i>=0;i--) {
		if(d[anc[x][i]]>=d[y]) x=anc[x][i];
	}
	if(x==y) return x;
	for(int i=20;i>=0;i--) {
		if(anc[x][i]!=anc[y][i]) {
			x=anc[x][i],y=anc[y][i];
		}
	}
	return anc[x][0];
}

int main() {
	read(n),read(m),read(rt);
	for(int i=1;i<n;i++) {
		int x,y;
		read(x),read(y);
		tr[x].push_back(y);
		tr[y].push_back(x);
	}
	
	dfs(rt,0);
	
	while(m--) {
		int x,y;
		read(x),read(y);
		printf("%d\n",findLCA(x,y));
	}
	
	return 0;
}

割点

注意:第一个节点的特判

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

#define read(x) scanf("%d",&x)
#define maxn 100000

int n,m;
vector<int> g[maxn+5];

int pre[maxn+5],low[maxn+5],cnt;
bool iscut[maxn+5];

void tarjan(int x,int fa) {
	pre[x]=low[x]=++cnt;
	int chd=0;
	for(int i=0;i<g[x].size();i++) {
		int y=g[x][i];
		if(y==fa) continue;
		if(!pre[y]) {
			chd++;
			tarjan(y,x);
			if(pre[x]<=low[y]&&fa) iscut[x]=true;
			low[x]=min(low[x],low[y]);
		} else if(pre[y]<pre[x]) low[x]=min(low[x],pre[y]);
	}
	if(fa==0&&chd>1) iscut[x]=true;
}

int main() {
	read(n),read(m);
	for(int i=1;i<=m;i++) {
		int x,y;
		read(x),read(y);
		g[x].push_back(y),g[y].push_back(x);
	}
	
	for(int i=1;i<=n;i++) {
		if(!pre[i]) tarjan(i,0);
	}
	
	int ans=0;
	for(int i=1;i<=n;i++) if(iscut[i]) ans++;
	printf("%d\n",ans);
	for(int i=1;i<=n;i++) if(iscut[i]) printf("%d ",i);
	
	return 0;
}

缩点

注意:找反边时注意要在同一次dfs中遍历到的点才能算,即需要判断col[y]==0

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

#define read(x) scanf("%d",&x)
#define maxn 100000

int n,m;
vector<int> g[maxn+5];
int v[maxn+5];

int pre[maxn+5],low[maxn+5],cnt=0;
stack<int> stk;

int col[maxn+5],sum,w[maxn+5];
vector<int> a[maxn+5];

int dist[maxn+5];

int dfs(int x) {
	if(dist[x]) return dist[x];
	for(int i=0;i<a[x].size();i++) {
		dist[x]=max(dist[x],dfs(a[x][i]));
	}
	dist[x]+=w[x];
	return dist[x];
}

void make_a() {
	for(int i=1;i<=n;i++) {
		for(int j=0;j<g[i].size();j++) {
			int y=g[i][j];
			if(col[i]!=col[y]) a[col[i]].push_back(col[y]);
		}
	}
}

void tarjan(int x) {
	pre[x]=low[x]=++cnt;
	stk.push(x);
	
	for(int i=0;i<g[x].size();i++) {
		int y=g[x][i];
		if(!pre[y]) {
			tarjan(y);
			low[x]=min(low[x],low[y]);
		} else if(pre[x]>pre[y]&&!col[y]) low[x]=min(low[x],pre[y]);
	}
	
	if(low[x]>=pre[x]) {
		int u;sum++;
		do{
			u=stk.top();stk.pop();
			col[u]=sum;
			w[sum]+=v[u];
		} while(u!=x);
	}
}

int main() {
	read(n),read(m);
	for(int i=1;i<=n;i++) read(v[i]);
	for(int i=1;i<=m;i++) {
		int x,y;
		read(x),read(y);
		g[x].push_back(y);
	}
	
	for(int i=1;i<=n;i++) {
		if(!pre[i]) tarjan(i);
	}
	
	make_a();
	
	for(int i=1;i<=sum;i++) if(!dist[i]) dfs(i);
	
	int ans=0;
	for(int i=1;i<=sum;i++) ans=max(ans,dist[i]);

	printf("%d",ans);
	
	return 0;
}

二分图匹配

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

#define read(x) scanf("%d",&x)

#define maxn 500
#define maxm ((int)5e4)

int n1,n2,m;
vector<int> a[maxn*2+5];
int match[maxn*2+5],use[maxn*2+5];

bool dfs(int x) {
	for(int i=0; i<a[x].size(); i++) {
		int y=a[x][i];
		if(use[y]) continue;
		use[y]=true;
		if(!match[y]||dfs(match[y])) {
			match[x]=y,match[y]=x;
			return true;
		}
	}
	return false;
}


int main() {
	read(n1),read(n2),read(m);
	for(int i=1; i<=m; i++) {
		int x,y;
		read(x),read(y);
		y+=maxn;
		a[x].push_back(y),a[y].push_back(x);
	}

	int s=0;
	for(int i=1; i<=n1; i++) {
		memset(use,0,sizeof(use));
		if(!match[i]) dfs(i);
	}
	for(int i=1; i<=n1; i++) if(match[i]) s++;
	
	printf("%d",s);

	return 0;
}

最大流·dinic

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

#define maxn 200
#define maxm 5000
#define inf 2147483647
#define ll long long
#define read(x) scanf("%d",&x)

struct Edge {
	int x,y,z;
	Edge() {}
	Edge(int _x,int _y,int _z) {
		x=_x,y=_y,z=_z;
	}
};

int n,m,S,T;

int h[maxn+5],nxt[maxm*2+5],cnt=-1;	//存图
Edge e[maxm*2+5];	//0~cnt

int cur[maxn+5];	//当前弧优化

int d[maxn+5];	//分层
queue<int> que;

void add_edge(int u,int v,int w) {
	e[++cnt]=Edge(u,v,w);
	nxt[cnt]=h[u];
	h[u]=cnt;
}

int bfs() {
	memset(d,0,sizeof(d));
	d[S]=1;
	que.push(S);

	while(!que.empty()) {
		int x=que.front();
		que.pop();
		for(int i=h[x]; ~i; i=nxt[i]) {
			int y=e[i].y,z=e[i].z;
			if(d[y]||z==0) continue;
			d[y]=d[x]+1;
			que.push(y);
		}
	}

	return d[T];
}

int dfs(int x,int w) {
	if(x==T) return w;	//找到增广路
	for(int& i=cur[x]; ~i; i=nxt[i]) {	//&!
		int y=e[i].y,z=e[i].z;
		if(d[y]!=d[x]+1||z==0) continue;
		int Min=dfs(y,min(w,z));	//更新当前增广路上的最小值
		if(Min>0) {
			e[i].z-=Min,e[i^1].z+=Min;
			return Min;
		}
	}
	return 0;
}

ll dinic() {
	ll ans=0;
	while(bfs()) {	//存在增广路
		for(int i=1; i<=n; i++) cur[i]=h[i];
		while(int x=dfs(S,inf)) ans+=x;
	}
	return ans;
}

int main() {
	memset(nxt,-1,sizeof(nxt));
	memset(h,-1,sizeof(h));

	read(n),read(m),read(S),read(T);
	for(int i=1; i<=m; i++) {
		int u,v,w;
		read(u),read(v),read(w);
		add_edge(u,v,w);
		add_edge(v,u,0);
	}

	ll ans=dinic();
	printf("%lld",ans);

	return 0;
}

费用流·dinic

注意:dfs的时候需要记录访问过的点,不要再次访问。最大流本来就是分层图,所以不需要。

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

#define read(x) scanf("%d",&x)

struct Edge{
	int x,y,z,f;
	Edge() {}
	Edge(int _x,int _y,int _z,int _f) {x=_x,y=_y,z=_z,f=_f;}
};

#define maxn ((int)5e3)
#define maxm ((int)5e4)

int n,m,S,T;
Edge e[maxm*2+5];
int h[maxn+5],nxt[maxm*2+5],cnt=-1;


void add_edge(int x,int y,int z,int f) {
	e[++cnt]=Edge(x,y,z,f);
	nxt[cnt]=h[x];
	h[x]=cnt;
}

queue<int> que;
int d[maxn+5];
bool inque[maxn+5];

bool spfa() {
	for(int i=1;i<=n;i++) d[i]=(1<<30);
	que.push(S);d[S]=0,inque[S]=true;
	while(!que.empty()) {
		int x=que.front();que.pop();
		inque[S]=false;
		
		for(int i=h[x]; ~i; i=nxt[i]) {
			int y=e[i].y,z=e[i].z,f=e[i].f;
			if(z&&d[x]+f<d[y]) {
				d[y]=d[x]+f;
				inque[y]=true,que.push(y);
			}
		}
	}
	
	return d[T]!=(1<<30);
}

int cur[maxn+5];
int sumw=0,ans=0;
bool use[maxn+5];

int dfs(int x,int w) {
	use[x]=true;
	if(x==T) return w;
	for(int& i=cur[x]; ~i; i=nxt[i]) {
		int y=e[i].y,z=e[i].z,f=e[i].f;
		if(use[y]||d[y]!=d[x]+f||z==0) continue;
		int Min=dfs(y,min(w,z));
		if(Min) {
			e[i].z-=Min,e[i^1].z+=Min;
			sumw+=(f*Min);
			return Min;
		}
	}
	return 0;
}

void dinic() {
	while(spfa()) {
		memset(use,0,sizeof(use));
		for(int i=1;i<=n;i++) cur[i]=h[i];
		while(int x=dfs(S,(1<<30))) ans+=x;
	}
}

int main() {
	memset(h,-1,sizeof(h)),memset(nxt,-1,sizeof(nxt));
	
	read(n),read(m),read(S),read(T);
	for(int i=1;i<=m;i++) {
		int x,y,z,f;
		read(x),read(y),read(z),read(f);
		add_edge(x,y,z,f);
		add_edge(y,x,0,-f); 
	}
	
	dinic(); 
	printf("%d %d",ans,sumw);
	
	return 0;
} 

树状数组

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

typedef long long ll;
typedef pair<int,int> pii;
typedef pair<ll,ll> pll;

namespace BIT{
	typedef long long ll;
	const int N=2e6;
	const ll inf=0x3f3f3f3f3f3f3f3f;
	
	int n;
	ll c[N+5];
	
	void init(int _n) {
		n=_n;
		for(int i=1;i<=n;i++) c[i]=0;
	}
	
	int lowbit(int x) {
		return x&-x;
	}
	
	void modify(int x,int d) {	//单点修改 
		for(int i=x;i<=n;i+=lowbit(i)) {
			c[i]+=d;
		}
	}
	
	ll query(int x) {	//单点询问 
		ll s=0;
		for(int i=x;i>0;i-=lowbit(i)) {
			s=s+c[i];
		}
		return s;
	}
}

线段树

namespace SEGMENT_TREE{
//	const int N=3e5;
	ll c[N+5];
	ll a[N*4+5],lzy[N*4+5];
	
	#define lson (o*2)
	#define rson (o*2+1)
	#define mid ((l+r)>>1)
	
	void push_down(int o,int l,int r) {
		a[lson]+=(mid-l+1)*lzy[o],a[rson]+=(r-mid)*lzy[o];
		lzy[lson]+=lzy[o],lzy[rson]+=lzy[o];
		lzy[o]=0;
		return ;
	}
	
	void push_up(int o) {
		a[o]=a[lson]+a[rson];
		return ;
	}
	
	void build(int o,int l,int r) {	//建树 
		if(l==r) {
			a[o]=c[l];
			return ;
		}
		build(lson,l,mid),build(rson,mid+1,r);
		push_up(o);
		return ;
	}
	
	void modify(int o,int l,int r,int L,int R,ll d) {	//区间修改 
		if(l>R||r<L) return ;
		if(l>=L&&r<=R) {
			a[o]+=d*(r-l+1);
			lzy[o]+=d;
			return ;
		}
		push_down(o,l,r);
		modify(lson,l,mid,L,R,d),modify(rson,mid+1,r,L,R,d);
		push_up(o);
		return ;
	}
	
	ll query(int o,int l,int r,int L,int R) {	//区间查询 
		if(l>R||r<L) return 0;
		if(l>=L&&r<=R) {
			return a[o]; 
		}
		push_down(o,l,r); 
		ll s=query(lson,l,mid,L,R)+query(rson,mid+1,r,L,R);
		return s;
	}
}

莫队

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

#define maxn 50000
#define ll long long
#define read(x) scanf("%d",&x)

struct Pair{
	int x,y;
	int Id;
	bool insq;
	Pair(){}
};

int cc;

bool cmp(const Pair& x,const Pair& y) {
	return x.x/cc==y.x/cc?x.y<y.y:x.x<y.x;
}

int n,m;
int a[maxn+5];
Pair q[maxn+5];

int ans[maxn+5];
int cnt[maxn+5];

int pans[maxn+5];
ll pans2[maxn+5];

int gcd(ll x,ll y) {
	if(y==0) return x;
	return gcd(y,x%y);
}

int main() {
	read(n),read(m);
	for(int i=1;i<=n;i++) read(a[i]);
	for(int i=1;i<=m;i++) read(q[i].x),read(q[i].y),q[i].Id=i;
	
	cc=sqrt(n);
	sort(q+1,q+m+1,cmp);
	
	int L=1,R=0;
	for(int i=1;i<=m;i++) {
		ans[i]=ans[i-1];
		while(L<q[i].x) ans[i]+=1-2*cnt[a[L]],cnt[a[L]]--,L++;
		while(L>q[i].x) L--,ans[i]+=2*cnt[a[L]]+1,cnt[a[L]]++;
		while(R>q[i].y) ans[i]+=1-2*cnt[a[R]],cnt[a[R]]--,R--;
		while(R<q[i].y) R++,ans[i]+=2*cnt[a[R]]+1,cnt[a[R]]++;
		if(L==R) {pans[q[i].Id]=0,pans2[q[i].Id]=1;continue;}
		pans[q[i].Id]=ans[i]-(q[i].y-q[i].x+1),pans2[q[i].Id]=(q[i].y-q[i].x+1)*((ll)q[i].y-q[i].x);
	}
	
	for(int i=1;i<=m;i++) {
		ll x=pans[i],y=pans2[i];
		ll g=gcd(x,y);
		printf("%lld/%lld\n",x/g,y/g);
	}
	
	return 0;
}


带修莫队

块的大小 n 2 / 3 n^{2/3} n2/3,复杂度 O ( n 5 / 3 ) O(n^{5/3}) O(n5/3)

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

#define maxn 1000000
#define read(x) scanf("%d",&x)

struct Q{
	int l,r;
	int Id,lstc,insq;
	Q(){}
};

struct C{
	int x,v;
	C(){}
};

int n,m;
int a[maxn+5];

Q q[maxn+5];
C c[maxn+5];
int mq,mc;
int cc;

bool cmp(const Q& x,const Q& y) {
	if(x.l/cc!=y.l/cc) return x.l<y.l;
	if(x.r/cc!=y.r/cc) return x.r<y.r;
	return x.lstc<y.lstc;
}

int cnt[maxn+5],s=0;
int ans[maxn+5];

void Add(int x) {if(!cnt[x]++) s++;}
void Del(int x) {if(!--cnt[x]) s--;}
void Work(int i,int t) {
	if(c[t].x>=q[i].l&&c[t].x<=q[i].r) {
		if(!--cnt[a[c[t].x]]) s--;
		if(!cnt[c[t].v]++) s++;
	}
	swap(c[t].v,a[c[t].x]);
}

int main() {
	read(n),read(m);
	for(int i=1;i<=n;i++) read(a[i]);
	for(int i=1;i<=m;i++) {
		char opr;
		while(scanf("%c",&opr)&&!isalpha(opr));
		if(opr=='Q') {
			read(q[++mq].l),read(q[mq].r);
			q[mq].Id=mq,q[mq].lstc=mc;
		}
		else {
			read(c[++mc].x),read(c[mc].v);
		}
	}
	
	cc=(int)pow(n,2.0/3);
	sort(q+1,q+mq+1,cmp);
	
	int L=1,R=0,T=0;
	for(int i=1;i<=mq;i++) {
		while(L>q[i].l) Add(a[--L]);
		while(L<q[i].l) Del(a[L++]);
		while(R>q[i].r) Del(a[R--]);
		while(R<q[i].r) Add(a[++R]);
		while(T<q[i].lstc) Work(i,++T);
		while(T>q[i].lstc) Work(i,T--);
		
		ans[q[i].Id]=s;
	}
	
	for(int i=1;i<=mq;i++) printf("%d\n",ans[i]);
	
	return 0;
}

评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值