暫存一些模板

retrogogogo發表於2020-12-22

  很久以前記得一些模板,一些自己寫的,一些可能是別的地方複製下來的,意義不大但是對以後的整理可能有幫助就先暫存在這了,侵刪

一、博弈

//1.巴什博弈(Bash Game)
//一堆n個物品,兩個人輪流從中取出1~m個,最後取光者勝(不能繼續取的人輸)。
//同餘定理:n=k*(m+1)+r,先者拿走r個,那麼後者無論拿走1 m個先者只要的數目使和為m+1,那麼先手必贏。反之若n=k*(m+1),那麼先手無論怎樣都會輸。
bool bashGame(int n,int m){
	if (n % (m + 1))  return false;
	else  return true;
}


//2.斐波那契博弈(Fibonacci Nim Game)
//一堆石子有n個,兩人輪流取,先取者第一次可以去任意多個,但是不能取完,以後每次取的石子數不能超過上次取子數的2倍。取完者勝。
//同樣是一個規律:先手勝當且僅當n不是斐波那契數。
bool fibonacciGame(int n){
	f[0] = f[1] = 1;
	for (int i = 0; f[i - 1] < n; i++)
	{
	    f[i] = f[i - 1] + f[i - 2];
	    if (f[i] == n)  return true;
	}
	return false;	
}


//3.威佐夫博弈(Wythoff Game)
//有兩堆各若干物品,兩個人輪流從任意一堆中至少取出一個或者從兩堆中取出同樣多的物品,規定每次至少取一個,至多不限,最後取光者勝。
//這裡的必輸局勢:(0,0)、(1,2)、(3,5)、(4,7)、(6,10)、(8,13)、(9,15)、(11,18)、(12,20)。
//從這些必輸局勢可以發現,每組的第一個是前面沒有出現的最小正整數,ak=[k*(1+sqrt(5)/2], bk=ak+k, k=0,1,2,3...。
bool wythoffGame(int a,int b){
	double r = (sqrt(5) + 1) / 2;
	int d = abs(a - b) * r;
	if (d != min(a, b))  return true;
	else  false;
}

//4.尼姆博弈(Nim Game)
//有n堆物品,兩人輪流取,每次取某堆中不少於1個,最後取完者勝。
//通過驗證所有的堆數量累^後只要為0就都是必輸局勢,所以我們就只要記住這個規則:
//將n堆物品數量全部異或後結果為0先手必敗,否則必勝。
bool NimGame(int arr[]){
	int res = 0;
	for (int i = 1; i <= n; i++)
	    res ^= arr[i];
	if (res)  return true;
	else  return false;	
}

//5.SG函式 
//待續

二、最小生成樹

//Prim模板
//題目連結 : http://poj.org/problem?id=1287
#include <stdio.h>
#include <string.h>
#include <vector>
#include <queue>

using namespace std;
const int maxn = 100 + 10;
const int INF = 1<<30;

struct Node{
    int v,w;
    Node (){}
    Node (int v,int w):v(v),w(w){}
    bool operator < (const Node&rhs ) const {
        return rhs.w < w;
    }
};

int n,d[maxn];
bool vis[maxn];
int Map[maxn][maxn];

void init(){
    for(int i = 0; i < maxn; i++)vis[i] = false;
    for(int i = 0; i < maxn; i++)d[i] = INF;
}

int Prim(int s){
    priority_queue<Node>q;
    q.push(Node(s,0));
    int ans = 0;
    while(!q.empty()){
        Node Now = q.top(); q.pop();
        int v = Now.v;
        if(vis[v]) continue;
        ans += Now.w;
        vis[v] = true;
        for(int i = 1; i <= n; i++){
            int w2 = Map[v][i];
            if(!vis[i] && d[i] > w2){
                d[i] = w2;
                q.push(Node(i,d[i]));
            }
        }
    }
    return ans;
}

int main(){
    //freopen("in.txt","r",stdin);
    int m,a,b,c;
    while(~scanf("%d",&n)&&n){
        scanf("%d",&m);
        init();
        for(int i = 1; i <= n; i++){
            Map[i][i] = INF;
            for(int j = 1; j < i; j++)Map[i][j] = Map[j][i] = INF;
        }
        for(int i = 0; i < m; i++){
            scanf("%d%d%d",&a,&b,&c);
            if(c < Map[a][b])Map[a][b] = Map[b][a] = c; //注意重邊,取小的
        }
        printf("%d\n",Prim(1));
    }
    return 0;
}
//kruskal模板
//題目連結 : http://poj.org/problem?id=1287
#include <stdio.h>
#include <string.h>
#include <algorithm>
#include <queue>

using namespace std;
const int maxn = 1e5 + 10;

int Fa[maxn],Rank[maxn];

void init(){
    for(int i = 0; i <= maxn; i++)Fa[i] = i;
    for(int i = 0; i <= maxn; i++)Rank[i] = 1;
}

int Find(int v){
    return Fa[v] == v ? v : Fa[v] = Find(Fa[v]);
}

void Union(int x, int y){
    int fx = Find(x);
    int fy = Find(y);
    if (fx == fy)return;
    if (Rank[fx] < Rank[fy])
        Fa[fx] = fy;
    else{
        Fa[fy] = fx;
        if (Rank[fx] == Rank[fy])Rank[fy]++;
    }
}

struct Edge{
    int u,v,w;
    Edge(){}
    Edge(int u,int v,int w):u(u),v(v),w(w){}
}edge[maxn*2];


int cmp(const void *a,const void *b){
    Edge *aa = (Edge*)a;
    Edge *bb = (Edge*)b;
    return aa->w > bb->w ? 1 : -1; //降序
}

int krusal(int n,int m){
    qsort(edge,m,sizeof(edge[0]),cmp);
    int ans = 0;
    int cnt = 0;
    for(int i = 0; i < m; i++){
        int u = edge[i].u;
        int v = edge[i].v;
        if(Find(u) != Find(v)){
            Union(u,v);
            cnt++;
            ans += edge[i].w;
        }
        if(cnt == n-1)break;
    }
    return ans;
}

int main(){
    //freopen("in.txt","r",stdin);
    int n,m;
    while(~scanf("%d",&n)&&n){
        scanf("%d",&m);
        init();
        for(int i = 0; i < m; i++) scanf("%d%d%d",&edge[i].u,&edge[i].v,&edge[i].w); //注意這題有重邊但是沒事
        printf("%d\n",krusal(n,m));
    }
    return 0;
}

三、計算幾何

//計算幾何 
struct node {  
    double x; // 橫座標  
    double y; // 縱座標  
}; 
//1.向量運算 
typedef node Vector;
 
Vector operator + (Vector A, Vector B) { return Vector(A.x + B.x, A.y + B.y); }  
Vector operator - (Point A, Point B) { return Vector(A.x - B.y, A.y - B.y); }  
Vector operator * (Vector A, double p) { return Vector(A.x*p, A.y*p); }  
Vector operator / (Vector A, double p) { return Vector(A.x / p, A.y*p); }  
 
double Dot(Vector A, Vector B) { return A.x*B.x + A.y*B.y; } // 向量點乘  
double Length(Vector A) { return sqrt(Dot(A, A)); }  // 向量模長  
double Angle(Vector A, Vector B) { return acos(Dot(A, B) / Length(A) / Length(B)); }  // 向量之間夾角  
 
double Cross(Vector A, Vector B) { // 叉積計算 公式  
    return A.x*B.y - A.y*B.x;  
}  
 
Vector Rotate(Vector A, double rad) // 向量旋轉 公式  {  
    return Vector(A.x*cos(rad) - A.y*sin(rad), A.x*sin(rad) + A.y*cos(rad));  
}  
 
Point getLineIntersection(Point P, Vector v, Point Q, Vector w) { // 兩直線交點t1 t2計算公式   
    Vector u = P - Q;   
    double t = Cross(w, u) / Cross(v, w);  // 求得是橫座標  
    return P + v*t;  // 返回一個點  
}  


//2.多邊形面積 
node G[maxn];  
int n;  
 
double Cross(node a, node b) { // 叉積計算  
    return a.x*b.y - a.y*b.x;  
}  
int main()  
{  
    while (scanf("%d", &n) != EOF && n) {  
        for (int i = 0; i < n; i++)   
            scanf("%lf %lf", &G[i].x, &G[i].y);  
        double sum = 0;  
        G[n].x = G[0].x;  
        G[n].y = G[0].y;  
        for (int i = 0; i < n; i++) {   
                sum += Cross(G[i], G[i + 1]);  
        }  
        // 或者  
            //for (int i = 0; i < n; i++) {  
                //sum += fun(G[i], G[(i + 1)% n]);  
            //}  
        sum = sum / 2.0;  
        printf("%.1f\n", sum);  
    }  
    return 0;  
}


//3.判斷線段相交
node P[35][105];     
double Cross_Prouct(node A,node B,node C) {     //  計算BA叉乘CA     
    return (B.x-A.x)*(C.y-A.y)-(B.y-A.y)*(C.x-A.x);      
}      
bool Intersect(node A,node B,node C,node D)  {  //  通過叉乘判斷線段是否相交;           
    if(min(A.x,B.x)<=max(C.x,D.x)&&         //  快速排斥實驗;      
       min(C.x,D.x)<=max(A.x,B.x)&&      
       min(A.y,B.y)<=max(C.y,D.y)&&      
       min(C.y,D.y)<=max(A.y,B.y)&&      
       Cross_Prouct(A,B,C)*Cross_Prouct(A,B,D)<0&&      //  跨立實驗;      
       Cross_Prouct(C,D,A)*Cross_Prouct(C,D,B)<0)       //  叉乘異號表示在兩側;      
       return true;      
    else return false;      
} 

//4.三角形外心
 Point circumcenter(const Point &a, const Point &b, const Point &c) { //返回三角形的外心        
    Point ret;  
    double a1 = b.x - a.x, b1 = b.y - a.y, c1 = (a1*a1 + b1*b1) / 2;  
    double a2 = c.x - a.x, b2 = c.y - a.y, c2 = (a2*a2 + b2*b2) / 2;  
    double d = a1*b2 - a2*b1;  
    ret.x = a.x + (c1*b2 - c2*b1) / d;  
    ret.y = a.y + (a1*c2 - a2*c1) / d;  
    return ret;  
}

四、並查集

//並查集
int father[maxn];   //  儲存i的father父節點  
void makeSet() {  
    for (int i = 0; i < maxn; i++)   
        father[i] = i;  
}  
int findRoot(int x) {   //  迭代找根節點
    int root = x; // 根節點  
    while (root != father[root]) { // 尋找根節點  
        root = father[root];  
    }  
    while (x != root) {  
        int tmp = father[x];  
        father[x] = root; // 根節點賦值 (路徑上的每個節點都可以直接連線到根上)
        x = tmp;  
    }  
    return root;  
}  
void Union(int x, int y) {  //  將x所在的集合和y所在的集合整合起來形成一個集合。  
    int a, b;  
    a = findRoot(x);  
    b = findRoot(y);  
    father[a] = b;  // y連在x的根節點上   或father[b] = a為x連在y的根節點上;  
}  

五、矩陣運算

六、DP問題
揹包
最長公共子序列

七、數論

歐幾里得及擴充

#include<iostream>
using namespace std;
//歐幾里得演算法:
int gcd(int a, int b)
{
	if (a < b) swap(a, b);
	return b == 0 ? a : gcd(b, a % b);
}

//擴充歐幾里得:
//既可以求出最大公約數,還可以順帶求解出使得: a* x + b * y = gcd 的特解 x 和 y
//最後狀態為x=1,y=0,以此遞推求出最初狀態x與y
//前狀態x1,y1與後狀態x2,y2關係公式
//x1 = y2
//y1 = x2 – a / b * y2
int exgcd(int a, int b, int& x1, int& y1)
{
	if (!b)
	{
		x1 = 1, y1 = 0;
		return a;
	}
	int x2, y2;
	int d = exgcd(b, a % b, x2, y2);
	x1 = y2, y1 = x2 - (a / b) * y2;
	return d;
}
int main()
{
	int a, b, x, y;
	while (cin >> a >> b >> x >> y) {
		cout << exgcd(a, b, x, y) << endl;
		cout << x << ' ' << y << endl;
	}

	return 0;
}

快速冪 尤拉函式及降冪

#include<iostream>
#include<math.h>
using namespace std;
typedef long long ll;
char b[1000010];

//快速冪演算法(對mod取模)
ll fastPower(ll base, ll power,ll mod)
{
	ll ans = 1;
	base %= mod;
	while (power > 0) {
		if (power & 1) {//指數為奇數,先乘
			ans = ans * base % mod;
		}
		power >>= 1;//指數取半
		base = (base * base) % mod;//基數平方
	}
	return ans;
}



//尤拉函式phi(n),表示小於等於n中與n互質的數的數量
//尤拉定理 對於任意互素的 a 和 n,有 a^phi(n)≡1(mod n)
//		得到通式:phi(n) = n * (1 - 1 / p1) * (1 - 1 / p2) *...* (1 - 1 / pn);p為n的所有質因數
ll euler_phi(ll n)
{
	ll ans = n;
	for (int i = 2; i <= sqrt(n); i++) {
		if (n % i == 0)//i是n的因數
		{
			ans -= ans / i;//通式公式
			while (n % i == 0)
				n /= i;//消去i因子
		}
	}
	if (n > 1)  ans -= ans / n;//判斷是否還剩下因子
	return  ans;
}
//尤拉降冪 a^b mod c = a^(b mod phi(c) + phi(c)) mod c;
ll eularDropPow(ll base, char pow[], ll mod)
{
	ll phi = euler_phi(mod);//得到尤拉函式值(phi(mod))
	ll newPow = 0;
	for (ll i = 0, len = strlen(pow); i < len; i++) {
		newPow = (newPow * 10 + pow[i] - '0') % phi;//每一次迴圈都取模防止溢位;
	}
	newPow += phi;//根據降冪公式,加上phi(mod),得到最終的新指數
	return newPow;//返回降冪後的冪指數
}
int main()
{
	ll a, c;
	while (cin >> a >> b >> c) {
		ll newPow = eularDropPow(a, b, c);
		cout << fastPower(a, newPow, c) << endl;
	}
	return 0;
}


組合數學等等


爭取寒假全都搞出來再系統地發一篇吧,不過模板還是自己整理的有用,現在的比賽也越來越不需要模板了,更多的作用可能在於加深印象,輔助做題吧。。

相關文章