1. 程式人生 > >ACM演算法模板--BY Focus

ACM演算法模板--BY Focus

1.數學

1.1素數

1.1.1素數篩法(尤拉篩法,判斷<maxn的數是否是素數及求取<maxn的素數)

/*
 *notprime是一張表,false表示是素數,true表示不是素數
 *prime是素數表,儲存小於maxn的全部素數。
 */
const int maxn = 10500000;
int prime[maxn];
bool notprime[maxn];

int Euler(int _n) //尤拉篩法求素數,時間複雜度為O(n)。
{
	int cnt = 0;
	memset(notprime, 0, sizeof notprime);
	memset(prime, 0, sizeof prime);

	for (int i = 2; i <= _n; i++)
	{
		if(!notprime[i])
			prime[cnt++] = i;
		for (int j = 0; j < cnt && i*prime[j] <= _n; j++)
		{
			notprime[i*prime[j]] = true;
			if (i % prime[j] == 0) break;
		}
	}

	return cnt;
}

 1.1.2素數篩法(埃氏篩法,判斷<maxn的數是否是素數及求取<maxn的素數)

/*
 *notprime是一張表,false表示是素數,true表示不是素數
 *prime是素數表,儲存小於maxn的全部素數。
 */
const int maxn = 10500000;
int prime[maxn];
bool notprime[maxn];

int Eratosthenes(int _n) //埃拉託斯特尼篩法,時間複雜度為O(nloglogn).
{
	int cnt = 0;
	memset(notprime, 0, sizeof notprime);
	memset(prime, 0, sizeof prime);

	for(int i = 2; i <= _n; i++)
	{
		if(!notprime[i])
		{
			prime[cnt++] = i;
			for (int j = i+i; j <= _n; j += i)
			{
				notprime[j] = true;
			}
		}
	}

	return cnt;
}

1.2最大公約數

int gcd(int big, int small)
{
    if (small > big) swap(big, small);
    int temp;
    while (small != 0){ //  輾轉相除法
        if (small > big) swap(big, small);
        temp = big % small;
        big = small;
        small = temp;
    }
    return big;
}

1.3快速冪

1.3.1普通快速冪

int power(long long a, int n)
{
    long long ans = 1;
    while(n > 0) {
        if(n&1) {
            ans *= a;
            ans %= mod;
        }
        a *= a%mod;
        a %= mod;
        n /= 2;
    }
    return ans%mod;
}

1.3.2矩陣快速冪

書上

2.圖論

2.1最短路

2.1.1Dijkstra單源最短路

權值非負

int n, m, s, t;
int len;
struct road
{
	int u, v, cost;
	int next;
};
struct node
{
	int v;
	int len;
	int cost;
	node(int v, int l, int c):v(v),len(l),cost(c){}
	friend bool operator <(node a, node b)
	{
		return a.cost>b.cost;
	}
};
road G[maxn*5];
int head[maxn];
int dist[maxn];
bool vis[maxn];
void addroad(int u, int v, int cost)//加邊
{
	G[len].u = u;
	G[len].v = v;
	G[len].cost = cost;
	G[len].next = head[u];
	head[u] = len++;
}
void dij()
{
	fill(dist, dist+n+1, INF);
	dist[1] = 0;
	priority_queue<node> pque;
	pque.push(node(1,0));
	while(pque.size())
	{
		node p = pque.top(); pque.pop();
		int v = p.v;
		if(dist[v] < p.cost) continue;
		for(int i = head[v]; i!=-1; i = G[i].next)
		{
			road e = G[i];
			if(dist[e.v] > min(dist[e.v],max(dist[e.u], e.cost)))
			{
				dist[e.v] = min(dist[e.v],max(dist[e.u], e.cost));
				pque.push(node(e.v, dist[e.v] ) );
			}
		}
	}
}

2.1.2Bellman—Ford判環

int n, m, s, t;
int len;
struct road
{
	int u, v, cost;
	int next;
};
struct node
{
	int v;
	int len;
	int cost;
	node(int v, int l, int c):v(v),len(l),cost(c){}
	friend bool operator <(node a, node b)
	{
		return a.cost>b.cost;
	}
};
road G[maxn*5];
int head[maxn];
int dist[maxn];
bool vis[maxn];
void addroad(int u, int v, int cost)
{
	G[len].u = u;
	G[len].v = v;
	G[len].cost = cost;
	G[len].next = head[u];
	head[u] = len++;
}
int bellman_ford()
{
	int res;
	fill(dist, dist+n+1, INF);
	dist[1] = 0;
	for(int i = 0; i < n; i++)
	{
		for(int k = 0; k < len; k++)
		{
			road e = G[k];
			if(dist[e.v] > dist[e.u] + e.cost)
			{
				dist[e.v] = dist[e.u] + e.cost;
				if(i == n-1)
				{
					return 0;
				}
			}
		}
	}
	return 1;
}

2.1.3Floyd多源最短路

int G[maxn][maxn];//儲存e(i,j)的權值,不存在邊時設為INF,其中G[i][i]==0
void floyd()
{
    for(int k = 0; k <= mx; k++)
    {
        for(int i = 0; i <= mx; i++)
        {
            for(int j = 0; j <= mx; j++)
            {
                G[i][j] = min(G[i][j], G[i][k] + G[k][j]);
            }
        }
    }
}

2.1.4次短路演算法

int n, m, s, t;
int len;
struct road
{
	int u, v;
	int next;
	int cost;
};
struct node
{
	int v;
	int cost;
	node(int v, int c):v(v),cost(c){}
	friend bool operator <(node a, node b)
	{
		return a.cost>b.cost;
	}
};
road G[200500];
int head[maxn];
int dist[maxn];
int dist2[maxn];
void dij()
{
	fill(dist2, dist2+n+1,INF);
	fill(dist, dist+n+1,INF);
	dist[1] = 0;
	priority_queue<node> pque;
	pque.push(node(1, 0));
	while(pque.size())
	{
		node p = pque.top();pque.pop();
		int v = p.v;
		//if(dist2[v] < p.cost) continue;
		for(int i = head[v]; i != -1; i=G[i].next)
		{
			road e = G[i];
			int d = p.cost + e.cost;
			if(dist[e.v] > d)
			{
				swap(dist[e.v], d);
				pque.push(node(e.v, dist[e.v]));
			}
			if(dist2[e.v] > d && d > dist[e.v])
			{
				dist2[e.v] = d;
				pque.push(node(e.v, dist2[e.v]));
			}
		}
	}
}

2.1.5雙權值求花費

/*
 *注意:在本模板中 len表示路長,cost表示花費。
 */

int n, m, s, t;
int len;
struct road
{
	int u, v, len, cost;
	int next;
};
struct node
{
	int v;
	int len;
	int cost;
	node(int v, int l, int c):v(v),len(l),cost(c){}
	friend bool operator <(node a, node b)
	{
		if(a.len == b.len)
			return a.cost>b.cost;
		return a.len>b.len;
	}
};
road G[maxn*5];
int head[maxn];
int mx = -INF;
int dist[maxn];
int cost[maxn];
int path[maxn];
void addroad(int u, int v, int l, int cost)
{
	G[len].u = u;
	G[len].v = v;
	G[len].len = l;
	G[len].cost = cost;
	G[len].next = head[u];
	head[u] = len++;
}
int dij()
{
	int res = 0;
	fill(dist, dist+n+1, INF);
	fill(path, path+n+1, -1);
	fill(cost, cost+n+1, 0);
	dist[1] = 0;
	priority_queue<node> pque;
	pque.push(node(1, 0, 0));
	while(pque.size())
	{
		node p = pque.top(); pque.pop();
		int v = p.v;
		if(dist[v] < p.len) continue;
		for(int i = head[v]; i != -1; i = G[i].next)
		{
			road e = G[i];
			if(dist[e.v] > dist[e.u] + e.len)
			{
				dist[e.v] = dist[e.u] + e.len;
				cost[e.v] = cost[e.u] + e.cost;
				path[e.v] = i;
				pque.push(node(e.v, dist[e.v], cost[e.v]));
			}
			else if(dist[e.v] == dist[e.u] + e.len)
			{
				if(path[e.v] != -1 && G[path[e.v]].cost > e.cost)
				{
					path[e.v] = i;
					pque.push(node(e.v, dist[e.v], cost[e.v]));
				}
			}
		}
	}
	for(int i = 1; i <= n; i++)
	{
		if(path[i]!=-1)
		{
			res += G[path[i]].cost;
		}
	}
	return res;
}

2.2最小(大)生成樹

2.2.1Prim演算法

int n, m, s, t;
int road[maxn][maxn];//儲存e(i,j)的權值,不存的邊設為INF
bool vis[maxn];//標記是否屬於集合X
int dist[maxn];//從集合X出發到每個頂點最小距離
int prim()
{
	memset(vis, 0, sizeof vis);
	fill(dist, dist+maxn, INF);
	dist[1] = 0;
	int res = 0;
	while(1)
	{
		int v = -1;
		//從不屬於X的定點中取出從距X最近的定點
		for(int i = 1; i <= n; i++)
		{
			if(!vis[i] &&(v==-1|| dist[i] < dist[v])) v = i;
		}
		if(v == -1) break;
		vis[v] = true;//把頂點v加入X
		res += dist[v];//更新結果
		for(int i = 1; i <= n; i++)
		{
			dist[i] = min(dist[i], road[v][i]);//更新dist陣列
		}
	}
	return res;
}

2.2.2Kruskal演算法

int n, m, s, t;
int len;
struct road
{
	int u, v, cost;
};
road G[maxn*5];
int par[maxn<<1];
void init()
{
	for(int i = 0; i < maxn<<1; i++) par[i] = i;
}

int Find(int x)
{
	return par[x]==x? x : par[x] = Find(par[x]);
}
bool same(int x, int y)
{
	int fx = Find(x), fy = Find(y);
	if(fx == fy)return true;
	else        return false;
}
void unite(int x, int y)
{
	int fx = Find(x), fy = Find(y);
	if(fx == fy) return;
	else{
		par[fx] = fy;
		return;
	}
}
bool cmp(road a, road b)
{
	return a.cost>b.cost;
}
int kruskal()
{
	sort(G, G+len, cmp);
	init();
	int res = 0;
	for(int i = 0; i < len; i++)
	{
		road e = G[i];
		if(!same(e.u, e.v))
		{
			unite(e.u, e.v);
			res+=e.cost;
		}
	}
	return res;
}

2.3強連通分量

2.3.1Tarjan演算法

int n, m;
struct edge
{
	int u, v;
	int next;
};
edge G[maxn<<3];
int h[maxn];
int dfn[maxn];
int low[maxn];
int color[maxn];
bool vis[maxn];
int tot, len, out, sum;
stack<int> sta;
void init()
{
	fill(h, h+maxn, -1);
	fill(dfn, dfn+maxn, 0);
	fill(vis, vis+maxn, 0);
	fill(isout, isout+maxn, 0);
	fill(color, color+maxn, 0);
	tot = 0;
	len = 0;
	sum = 0;
	out = 0;
}
void tarjan(int u)
{
	dfn[u] = low[u] = ++tot;
	vis[u] = true;
	sta.push(u);

	for(int i = h[u]; ~i; i = G[i].next)
	{
		edge e = G[i];
		if(!dfn[e.v])
		{
			tarjan(e.v);
			low[u] = min(low[u], low[e.v]);
		}
		else if(vis[e.v])
		{
			low[u] = min(low[u], dfn[e.v]);
		}
	}

	if(dfn[u] == low[u])
	{
		int x;
		++sum;
		do
		{
			x = sta.top(); sta.pop();
			vis[x] = false;
			color[x] = sum;
		}
		while(x != u);
	}
}

2.3.2 Kosaraju演算法(解決2-SAT問題)

int n, m;
vector<int> G[maxn];
vector<int> rG[maxn];
vector<int> vs;
bool used[maxn];
int cmp[maxn];
int s[maxn], t[maxn], d[maxn];

void add_edge(int from, int to)
{
	G[from].pb(to);
	rG[to].pb(from);
}
void dfs(int v)
{
	used[v] = true;
	for(int i = 0; i < G[v].size(); i++)
	{
		if(!used[G[v][i]])
			dfs(G[v][i]);
	}
	vs.pb(v);
}
void rdfs(int v, int k)
{
	used[v] = true;
	cmp[v] = k;
	for(int i = 0; i < rG[v].size(); i++)
	{
		if(!used[rG[v][i]])
			rdfs(rG[v][i], k);
	}
}
int ssc()
{
	memset(used, 0, sizeof used);
	for(int i = 0; i < n<<1; i++)
	{
		if(!used[i])
			dfs(i);
	}

	memset(used, 0, sizeof used);
	int k = 0;
	for(int i = vs.size()-1; i >= 0; i--)
	{
		if(!used[vs[i]])
			rdfs(vs[i], k++);
	}
	return k;
}
/*
 *如果需要輸出結果,變數i為真滿足的條件為cmp[i]>cmp[i+n],否則i為假
 */

3.資料結構

3.1線段樹

3.1.1單點更新的線段樹

int n, m, t, _n;
int dat[maxn<<2];
void build(int root, int left, int right)
{
	if(left == right)
	{
		dat[root] = 1;
		return;
	}

	int mid, rt;
	mid = (left+right)>>1;
	rt = root<<1;

	build(rt, left, mid);
	build(rt|1, mid+1, right);

	dat[root] = dat[rt] + dat[rt|1];
}

void Update(int pos, int val, int root, int left, int right)
{
	if(left==right)
	{
		dat[root] = val;
		return;
	}
	int m=(left+right)>>1;

	if(pos <= m)    Update(pos, val, root<<1, left, m);
	else            Update(pos, val, root<<1|1, m+1, right);
	dat[root] = dat[root<<1] + dat[root<<1|1];
}

int query(int qleft, int qright, int root, int left, int right)
{
	if(qleft<=left && right<=qright)
	{
		return dat[root];
	}
	int m = (left+right)>>1;

	int ans = 0;
	if(qleft <= m ) ans += query(qleft, qright, root<<1, left, m);
	if(qright > m)  ans += query(qleft, qright, root<<1|1, m+1, right);
	return ans;
}

3.1.2區間更新的線段樹

int n, m, t, _n;
struct node
{
	long long num;
	long long lazy;
};
node dat[maxn<<2];
long long arr[maxn];
void build(int k, int l, int r)
{
	if(l==r)
	{
		dat[k].num = arr[l];
		dat[k].lazy = 0;
		return;
	}

	build(k<<1, l, (l+r)/2);
	build(k<<1|1, (l+r)/2+1, r);

	dat[k].num = dat[k<<1].num + dat[k<<1|1].num;
}
void update(int a, int b, long long val, int k, int l, int r)//區間加減
{
	if(r<a || b<l) return;
	if(a<=l && r<=b)
	{
		dat[k].lazy += val;
		dat[k].num += (val*(r-l+1));
		return;
	}
	if(dat[k].lazy)
	{
		dat[k<<1].num += ((l+r)/2-l+1)*dat[k].lazy;
		dat[k<<1].lazy += dat[k].lazy;
		dat[k<<1|1].num += (r-(l+r)/2)*dat[k].lazy;
		dat[k<<1|1].lazy += dat[k].lazy;
		dat[k].lazy = 0;
	}
	update(a,b,val,k<<1,l,(l+r)/2);
	update(a,b,val,k<<1|1,(l+r)/2+1,r);
	dat[k].num = dat[k<<1].num + dat[k<<1|1].num;
}
long long query(int a, int b, int k, int l, int r)
{
	if(a<=l && r<=b)
		return dat[k].num;
	if(dat[k].lazy)
	{
		dat[k<<1].num += ((l+r)/2-l+1)*dat[k].lazy;
		dat[k<<1].lazy += dat[k].lazy;
		dat[k<<1|1].num += (r-(l+r)/2)*dat[k].lazy;
		dat[k<<1|1].lazy += dat[k].lazy;
		dat[k].lazy = 0;
	}
	int m = (l+r)/2;
	long long ans = 0;
	if(a<=m)    ans+=query(a,b,k<<1,l,m);
	if(b>m)     ans+=query(a,b,k<<1|1,m+1,r);
	return ans;
}

3.2二維樹狀陣列

int n, w, h, s, t;
int bit[maxn][maxn];

int lowbit(int x)
{
	return x&(-x);
}
void add(int x, int y, int v)
{
	for(int i = x; i <= h; i+=lowbit(i))
	{
		for(int j = y; j <= w; j+=lowbit(j))
		{
			bit[i][j] += v;
		}
	}
}
int query(int x, int y)
{
	int res = 0;
	for(int i = x; i; i-=lowbit(i))
	{
		for(int j = y; j; j-=lowbit(j))
		{
			res+=bit[i][j];
		}
	}
	return res;
}

4.字串處理

4.1KMP演算法

//未改進的KMP演算法程式碼實現
void get_next(int *next, char *T, int len)
{
	next[0] = -1;//-1代表沒有重複子串
	int k = -1;
	for (int q = 1; q <= len; q++)
	{
		while (k > -1 && T[k+1] != T[q])//下一個元素不相等,把k向前回溯
		{
			k = next[k];
		}
		if (T[k+1] == T[q])//下一個元素相等,所以最長重複子串+1
		{
			k = k+1;
		}
		next[q] = k;//給next陣列賦值
	}
}
int KMP(char *s, int len, char *p, int plen)//利用KMP演算法匹配
{
	int *next = new int(plen);
	get_next(next, p, plen);
	int k = -1;
	int i = 0;
	for (; i < len; i++)
	{
		while (k > -1 && p[k+1]!=s[i])//兩串下一個字元不相等,向前回溯(效率高就是在這裡,每次匹配失敗,
		//k不用直接變為0,從第一個字元開始重新匹配,而是變為最長重複子串的下一個字元,從中間開始匹配即可)。
		{
			k = next[k];
		}
		if(p[k+1] == s[i])//兩個串的字元相等,k+1來匹配子串的一個字元
		{
			k++;
		}
		if (k == plen-1)//匹配成功,返回短串在長串的位置。
		{
			return i-plen+1;

		}
	}
	return -1;
}

4.2Manacher演算法

/*
 * 求最長迴文子串
 */
const int MAXN=110010;
char Ma[MAXN*2];
int Mp[MAXN*2];
void Manacher(char s[],int len){
	int l=0;
	Ma[l++]='$';
	Ma[l++]='#';
	for(int i=0;i<len;i++){
		Ma[l++]=s[i];
		Ma[l++]='#';
	}
	Ma[l]=0;
	int mx=0,id=0;
	for(int i=0;i<l;i++){
		Mp[i]=mx>i?min(Mp[2*id−i],mx−i):1;
		while(Ma[i+Mp[i]]==Ma[i−Mp[i]])Mp[i]++;
		if(i+Mp[i]>mx){
			mx=i+Mp[i];
			id=i;
		}
	}
}
/*
 * abaaba
 * i: 0 1 2 3 4 5 6 7 8 9 10 11 12 13
 * Ma[i]: $ # a # b # a # a # b # a #
 * Mp[i]: 1 1 2 1 4 1 2 7 2 1 4 1 2 1
 */
char s[MAXN];
int main(){
	while(scanf("%s",s)==1){
		int len=strlen(s);
		Manacher(s,len);
		int ans=0;
		for(int i=0;i<2*len+2;i++)
			ans=max(ans,Mp[i]−1);
		printf("%d\n",ans);
		}
	return 0;
}

5.搜尋

5.1廣搜

int move[4][2] = {1,0,0,1,-1,0,0,-1};//遍歷四個方向
 
struct node  
{  
	int x, y, step;//x,y是座標,t是步數.
	node(int _x, int _y, int _step){x = _x; y= _y; step=_step;}  
	node(){} 
};
 
int h, w, sx, sy, gx, gy;
int map[500][500];  
bool visit[500][500];
 
int bfs()  
{  
	memset(visit, 0, sizeof(visit));  
	queue<node> que;//建立空佇列
	node cor(sx,sy,0); //壓入起點
	que.push(cor);
	while (!que.empty())  
	{  
		node next = que.front();//下一個佇列元素
		que.pop();//出隊
		for (int i = 0; i < 4; i++)  
		{  
			int nx = cor.x+move[i][0], ny = cor.y+move[i][1];  
			if (0<=nx&&0<=ny&&nx<h&&ny<w&&!visit[nx][ny]) //保證新元素不越界並且沒有訪問過
			{  
				visit[nx][ny] = true;  
				que.push(node(nx,ny,next.step+1));  
				if (nx == gx&& ny == gy)//滿足條件結束函式 
				{
					return next.step+1;  
				}  
			}  
		}  
		  
	}  
	return -1;  
}  

5.2二分查詢

/*
    |二分搜尋|
    |要求:先排序|
*/
 
//  l為最開始元素, r是末尾元素,x是要找的數

int bsearch(int *A, int l, int r, int val){
    int m;
    while (l < r){
		if(l==r)
		{
			if(A[l] == val)
				return l;
			else
				return -1;
		}
        m = (l + r) / 2;
        if (A[m] >= val)  r = m;   else l = m + 1;
    }
    return -1;
}
 
/*
    最後l == r  
    如果找有多少的x,可以用lower_bound查詢一遍,upper_bound查詢一遍,下標相減  
    C++自帶的lower_bound(a,a+n,x)返回陣列中最後一個x的下一個數的地址 
    upper_bound(a,a+n,x)返回陣列中第一個x的地址
    如果a+n內沒有找到x或x的下一個地址,返回a+n的地址  
    lower_bound(a,a+n,x)-upper_bound(a,a+n,x)返回陣列中x的個數
*/

6.其他

6.1標頭檔案及巨集定義

​

#include<bits/stdc++.h>
#include <set>
#include <map>
#include <cmath>
#include <queue>
#include <stack>
#include <vector>
#include <string>
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <malloc.h>
#include <iostream>
#include <algorithm>
#include <functional>
#define sdddd(x,y,z,k) scanf("%d%d%d%d", &x, &y, &z, &k)
#define sddd(x,y,z) scanf("%d%d%d", &x, &y, &z)
#define sdd(x,y) scanf("%d%d", &x, &y)
#define sd(x) scanf("%d", &x)
#define mp make_pair
#define pb push_back
#define lson k<<1
#define rson k<<1|1
#define mid (1+r)/2
#define ms(x, y) memset(x, y, sizeof x)
#define MOD 142857
using namespace std;
typedef long long ll;
typedef unsigned long long ull;
const int maxn = 1000050;
const int INF = 0x3f3f3f3f;
const ll LINF = 0x3f3f3f3f3f3f3f3f;

​

6.2輸入輸出加速

std::ios::sync_with_stdio(false);

6.3檔案輸入輸出

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

6.4STL庫

6.4.1set及multiset

//set及multiset用法(後者允許重複)
//主要函式:
begin() //返回指向第一個元素的迭代器
clear() //清除所有元素
count() //返回某個值元素的個數
empty() //如果集合為空,返回true
end() //返回指向最後一個元素的迭代器
erase() //刪除集合中的元素(引數是一個元素值,或者迭代器)
find() //返回一個指向被查詢到元素的迭代器
insert() //在集合中插入元素
size() //集合中元素的數目
lower_bound() //返回指向大於(或等於)某值的第一個元素的迭代器
upper_bound() //返回大於某個值元素的迭代器
equal_range() //返回集合中與給定值相等的上下限的兩個迭代器
//(注意對於multiset 刪除操作之間刪除值會把所以這個值的都刪掉,刪除一個要用迭代器)

6.4.2map

begin() //返回指向map頭部的迭代器 
clear()// 刪除所有元素 
count()// 返回指定元素出現的次數 
empty() //如果map為空則返回true 
end() //返回指向map末尾的迭代器 
equal_range()// 返回特殊條目的迭代器對 
erase() //刪除一個元素 
find() //查詢一個元素 
insert() //插入元素 
lower_bound() //返回鍵值>=給定元素的第一個位置 
max_size() //返回可以容納的最大元素個數 
rbegin() //返回一個指向map尾部的逆向迭代器 
rend() //返回一個指向map頭部的逆向迭代器 
size() //返回map中元素的個數 
swap()// 交換兩個map 
upper_bound()// 返回鍵值>給定元素的第一個位置 
value_comp() //返回比較元素value的函式
for(auto i = mp.begin(); i != mp.end(); i++)//遍歷,i為pair型別 訪問用'.'
map<int,string>::iterator it;
for(it = mp.begin(); it != mp.end(); it++)//遍歷,it為迭代器型別 訪問用'->'
for(auto it:mp)//遍歷,i為pair型別 訪問用'.'