比赛模板(持续更新中)

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/luomingjun12315/article/details/51254388

1、数据结构

(1)线段树单点更新

#include <cstdio>
#include <cstdlib>
#include <algorithm>
#include <cstring>
#include <cmath>
#define MAXN (1<<19)
using namespace std;

int segTree[MAXN];

void update(int i, int lft, int rht, int index, int deta){
    if(lft == rht){
        segTree[i] = deta;
        return ;
    }
    int mid = (lft + rht) >> 1;
    if(index <= mid) update(i<<1, lft, mid, index, deta);
    else update(i<<1|1, mid+1, rht, index, deta);
    segTree[i] = max(segTree[i<<1], segTree[i<<1|1]);
}
int query(int i, int lft, int rht, int qlft, int qrht){
    if(qlft <= lft && rht <= qrht) return segTree[i];
    int mid = (lft+rht)>>1;
    int ans = 0;
    if(qlft <= mid) ans = max(ans, query(i<<1, lft, mid, qlft, qrht));
    if(qrht > mid) ans = max(ans, query(i<<1|1, mid+1, rht, qlft, qrht));
    return ans;
}
int main(){
    char cmd[5];
    int n, m;
    while(scanf("%d%d", &n, &m) != EOF){
        memset(segTree, 0, sizeof(segTree));
        int a, b;
        for(int i = 0; i < n; i++){
            scanf("%d", &a);
            update(1, 1, n, i+1, a);
        }
        while(m--){
            scanf("%s %d %d", cmd, &a, &b);
            if(cmd[0] == 'Q') printf("%d\n", query(1, 1, n, a, b));
            else update(1, 1, n, a, b);
        }
    }
    return 0;
}

(2)线段树区间更新

#include <cstdio>
const int N = 1<<18;
typedef long long ll;
ll sum[N], add[N];

void pushDown(const int& i, const int& lft, const int& rht) {
    if(add[i]){
        add[i<<1] += add[i];
        add[i<<1|1] += add[i];
        int mid = (lft+rht)>>1;
        sum[i<<1] += add[i]*(mid-lft+1);
        sum[i<<1|1] += add[i]*(rht-mid);
        add[i] = 0;
    }
}

void update(int i, int lft, int rht, const int& qlft, const int& qrht,const int& addval) {
    if(qlft > rht || qrht < lft) return ;
    if(qlft <= lft && qrht >= rht){
        sum[i] += addval*(rht-lft+1);
        add[i] += addval;
    }
    else{
        pushDown(i, lft, rht);
        int mid = (lft + rht) >> 1;
        update(i<<1, lft, mid, qlft, qrht, addval);
        update(i<<1|1, mid+1, rht, qlft, qrht, addval);
        sum[i] = sum[i<<1] + sum[i<<1|1];
    }
}

ll query(int i, int lft, int rht, const int& qlft, const int& qrht) {
    if(qlft > rht || qrht < lft) return 0;
    if(qlft <= lft && qrht >= rht) return sum[i];
    pushDown(i, lft, rht);
    int mid = (lft + rht) >> 1;
    return query(i<<1, lft, mid, qlft, qrht) + query(i<<1|1, mid+1, rht, qlft, qrht);
}

int main() {
    int n, q, lft, rht;
    ll delta;
    char cmd[5];
    scanf("%d%d", &n, &q);
    for(int i = 1; i <= n; i++) {
        scanf("%lld", &delta);
        update(1, 1, n, i, i, delta);
    }
    while(q--) {
        scanf("%s", cmd);
        if(cmd[0] == 'Q'){
            scanf("%d%d", &lft, &rht);
            printf("%lld\n", query(1, 1, n, lft, rht));
        }else{
            scanf("%d%d%lld", &lft, &rht, &delta);
            update(1, 1, n, lft, rht, delta);
        }
    }
    return 0;
}

(3)树状数组

#include <cstdio>
#include <cstring>
#define MAXN 50000 + 10
using namespace std;

int Tree[MAXN];

int lowbit(int index){
    return index & (-index);
}
int sum(int index){
    int res = 0;
    while(index > 0){
        res += Tree[index];
        index -= lowbit(index);
    }
    return res;
}
void update(int index, int value, int n){
    while(index <= n){
        Tree[index] += value;
        index += lowbit(index);
    }
}

int main()
{
    char cmd[10];
    int a, b;
    int T, n, v;
    scanf("%d", &T);
    for(int nCase = 1; nCase <= T; nCase++){
        memset(Tree, 0, sizeof(Tree));
        printf("Case %d:\n", nCase);
        scanf("%d", &n);
        for(int i = 1; i <= n; i++){
            scanf("%d", &v);
            update(i, v, n);
        }
        do{
            scanf("%s", cmd);
            if(!strcmp(cmd, "Query")){
                scanf("%d%d", &a, &b);
                printf("%d\n", sum(b) - sum(a-1));
            }
            else if(!strcmp(cmd, "Add")){
                scanf("%d%d", &a, &b);
                update(a, b, n);
            }
            else if(!strcmp(cmd, "Sub")){
                scanf("%d%d", &a, &b);
                update(a, -b, n);
            }
        }while(strcmp(cmd, "End"));
    }
    return 0;
}

(4)归并树

#include <cstdio>
#include <algorithm>
#define MAXN (100000)
#define DEEP (20)
using namespace std;
int sorted[DEEP][MAXN], a[MAXN];
void build(int deep, int lft, int rht){
    if(lft == rht){
        sorted[deep][lft] = a[lft];
        return ;
    }
    int mid = (lft + rht) >> 1;
    build(deep+1, lft, mid);
    build(deep+1, mid+1, rht);
    int p = lft, q = mid+1, k = lft;
    while(p <= mid && q <= rht){
        if(sorted[deep+1][p] <= sorted[deep+1][q])
            sorted[deep][k++] = sorted[deep+1][p++];
        else sorted[deep][k++] = sorted[deep+1][q++];
    }
    while(p <= mid) sorted[deep][k++] = sorted[deep+1][p++];
    while(q <= rht) sorted[deep][k++] = sorted[deep+1][q++];
}
int query(int deep, int lft, int rht, int qlft, int qrht, int key){
    if(qrht < lft || qlft > rht) return 0;
    if(qlft <= lft && rht <= qrht)
        return lower_bound(&sorted[deep][lft], &sorted[deep][rht]+1, key) - &sorted[deep][lft];
    int mid = (lft + rht) >> 1;
    return query(deep+1, lft, mid, qlft, qrht, key) + query(deep+1, mid+1, rht, qlft, qrht, key);
}
int solve(int n, int qlft, int qrht, int k){
	int low = 1, high = n+1;
	while(low+1 < high){
		int mid= (low + high) >> 1;
		int cnt = query(0, 1, n, qlft, qrht, sorted[0][mid]);
		if(cnt <= k) low = mid;
		else high = mid;
	}
	return sorted[0][low];
}
int main(){
    int n, m;
    scanf("%d%d", &n, &m);
    for(int i = 1; i <= n; i++){
        scanf("%d", &a[i]);
    }
    build(0, 1, n);
    while(m--){
        int qlft, qrht, k;
        scanf("%d%d%d", &qlft, &qrht, &k);
        printf("%d\n", solve(n, qlft, qrht, k-1));
    }
    return 0;
}

(5)划分树

#include <cstdio>
#include <algorithm>
using namespace std;
const int MAXN = 100000 + 1;
const int DEEP = 18;
typedef struct{
    int num[MAXN];
    int cnt[MAXN];
}PartitionTree;
PartitionTree tree[DEEP];
int sorted[MAXN];

void build(int deep, int lft, int rht){
    if(lft == rht) return ;
    int mid = (lft + rht) >> 1;
    int key = sorted[mid];
    int scnt = mid - lft + 1;
    for(int i = lft; i <= rht; ++i){
        if(tree[deep].num[i] < key) --scnt;
    }
    int p = lft-1, r = mid;
    for(int i = lft, cnt = 0; i <= rht; ++i){
        int num = tree[deep].num[i];
        if(num < key || (num == key && scnt)){
            if(num == key) --scnt;
            ++cnt;
            tree[deep+1].num[++p] = num;
        }
        else tree[deep+1].num[++r] = num;
        tree[deep].cnt[i] = cnt;
    }
    build(deep+1, lft, mid);
    build(deep+1, mid+1, rht);
}

int query(int deep, int lft, int rht, int qlft, int qrht, int k){
    if(lft == rht) return tree[deep].num[lft];
    int mid = (lft + rht) >> 1;
    int left = 0, sum_in_left = tree[deep].cnt[qrht];
    if(lft != qlft){
        left = tree[deep].cnt[qlft-1];
        sum_in_left -= left;
    }
    if(sum_in_left >= k){
        int new_qlft = lft + left;
        int new_qrht = new_qlft + sum_in_left - 1;
        return query(deep+1, lft, mid, new_qlft, new_qrht, k);
    }
    else{
        int a = qlft - lft - left;
        int b = qrht - qlft - sum_in_left;
        int new_qlft = mid + 1 + a;
        int new_qrht = new_qlft + b;
        return query(deep+1, mid+1, rht, new_qlft, new_qrht, k-sum_in_left);
    }
}

int main(){
    int n, m, qlft, qrht, k;
    scanf("%d%d", &n, &m);
    for(int i = 1; i <= n; i++){
        scanf("%d", &sorted[i]);
        tree[0].num[i] = sorted[i];
    }
    sort(sorted+1, sorted+1+n);
    build(0, 1, n);
    while(m--){
        scanf("%d%d%d", &qlft, &qrht, &k);
        printf("%d\n", query(0, 1, n, qlft, qrht, k));
    }
    return 0;
}



2、图论

(1)并查集

int node[i]; //每个节点
int rank[i]; //树的高度

//初始化n个节点
void Init(int n){
    for(int i = 0; i < n; i++){
        node[i] = i;
        rank[i] = 0;
    }
}
//查找当前元素所在树的根节点(代表元素)
int find(int x){
    if(x == node[x])
        return x;
    return node[x] = find(node[x]); //在第一次查找时,将节点直连到根节点
}
//合并元素x, y所处的集合
void Unite(int x, int y){
    //查找到x,y的根节点
    x = find(x);
    y = find(y);
    if(x == y) 
        return ;
    //判断两棵树的高度,然后在决定谁为子树
    if(rank[x] < rank[y]){
        node[x] = y;
    }else{
        node[y] = x;
        if(rank[x] == rank[y]) rank[x]++:
    }
}
//判断x,y是属于同一个集合
bool same(int x, int y){
    return find(x) == find(y);
}

(2)最小生成树Kruskal

#include <cstdio>
#include <cstdlib>
#define MAXN 10000 + 10
using namespace std;

int par[MAXN], Rank[MAXN];
typedef struct{
    int a, b, price;
}Node;
Node a[MAXN];

int cmp(const void*a, const void *b){
    return ((Node*)a)->price - ((Node*)b)->price;
}
void Init(int n){
    for(int i = 0; i < n; i++){
        Rank[i] = 0;
        par[i] = i;
    }
}

int find(int x){
    int root = x;
    while(root != par[root]) root = par[root];
    while(x != root){
        int t = par[x];
        par[x] = root;
        x = t;
    }
    return root;
}

void unite(int x, int y){
    x = find(x);
    y = find(y);
    if(Rank[x] < Rank[y]){
        par[x] = y;
    }
    else{
        par[y] = x;
        if(Rank[x] == Rank[y]) Rank[x]++;
    }
}
//n为边的数量,m为村庄的数量
int Kruskal(int n, int m){
    int nEdge = 0, res = 0;
    //将边按照权值从小到大排序
    qsort(a, n, sizeof(a[0]), cmp);
    for(int i = 0; i < n && nEdge != m - 1; i++){
        //判断当前这条边的两个端点是否属于同一棵树
        if(find(a[i].a) != find(a[i].b)){
            unite(a[i].a, a[i].b);
            res += a[i].price;
            nEdge++;
        }
    }
    //如果加入边的数量小于m - 1,则表明该无向图不连通,等价于不存在最小生成树
    if(nEdge < m-1) res = -1;
    return res;
}
int main(){
    int n, m, ans;
    while(scanf("%d%d", &n, &m), n){
        Init(m);
        for(int i = 0; i < n; i++){
            scanf("%d%d%d", &a[i].a, &a[i].b, &a[i].price);
            //将村庄编号变为0~m-1(这个仅仅只是个人习惯,并非必要的)
            a[i].a--;
            a[i].b--;
        }
        ans = Kruskal(n, m);
        if(ans == -1) printf("?\n");
        else printf("%d\n", ans);
    }
    return 0;
}

(3)最小生成树prim

【未优化版】

#include <cstdio>
#include <vector>
#define INF 0xfffffff
#define MAXN 100 + 10
using namespace std;
struct Vex{
    int v, weight;
    Vex(int tv, int tw):v(tv), weight(tw){}
};
vector<Vex> graph[MAXN];
bool inTree[MAXN];
int mindist[MAXN];

void Init(int n){
    for(int i = 1; i <= n; i++){
        mindist[i] = INF;
        inTree[i] = false;
        graph[i].clear();
    }
}

int Prim(int s, int n){
    int addNode, tempMin, tempVex ,ret = 0;
    //将顶点S加入集合Vnew
    inTree[s] = true;
    //初始化,各点到集合Vnew的距离, 数组mindist表示各点到集合Vnew的最小距离
    for(unsigned int i = 0; i < graph[s].size(); i++)
        mindist[graph[s][i].v] = graph[s][i].weight;
    //因为还有n-1个点没有加入集合Vnew,所以还要进行n-1次操作
    for(int NodeCount = 1; NodeCount <= n-1; NodeCount++){
        tempMin = INF;
        //在还没有加入集合Vnew的点中查找距离集合Vnew最小的点
        for(int i = 1; i <= n; i++){
            if(!inTree[i] && mindist[i] < tempMin){
                tempMin = mindist[i];
                addNode = i;
            }
        }
        //将距离集合Vnew距离最小的点加入集合Vnew
        inTree[addNode] = true;
        //将新加入边的权值计入ret
        ret += tempMin;
        //更新还没有加入集合Vnew的点 到 集合Vnew的距离
        for(unsigned int i = 0; i < graph[addNode].size(); i++){
            tempVex = graph[addNode][i].v;
            if(!inTree[tempVex] && graph[addNode][i].weight < mindist[tempVex]){
                mindist[tempVex] = graph[addNode][i].weight;
            }
        }
    }
    return ret;
}

int main(){
    int n;
    int v1, v2, weight;
    while(scanf("%d", &n), n){
        Init(n);
        for(int i = 0; i < n*(n-1)/2; i++){
            scanf("%d%d%d", &v1, &v2, &weight);
            graph[v1].push_back(Vex(v2, weight));
            graph[v2].push_back(Vex(v1, weight));
        }
        printf("%d\n", Prim(1, n));
    }
    return 0;
}

【堆优化版】

#include <cstdio>
#include <vector>
#include <queue>
#define INF 0xfffffff
#define MAXN 100 + 10
using namespace std;
struct Vex{
    int v, weight;
    Vex(int tv = 0, int tw = 0):v(tv), weight(tw){}
    bool operator < (const Vex& t) const{
        return this->weight > t.weight;
    }
};
vector<Vex> graph[MAXN];
bool inTree[MAXN];
int mindist[MAXN];

void Init(int n){
    for(int i = 1; i <= n; i++){
        mindist[i] = INF;
        inTree[i] = false;
        graph[i].clear();
    }
}

int Prim(int s, int n){
    priority_queue<Vex> Q;
    Vex temp;
    //res用来记录最小生成树的权值之和
    int res = 0;
    //将s加入集合Vnew,并更新与点s相连接的各点到集合Vnew的距离
    inTree[s] = true;
    for(unsigned int i = 0; i < graph[s].size(); i++){
        int v = graph[s][i].v;
        if(graph[s][i].weight < mindist[v]){
            mindist[v] = graph[s][i].weight;
            //更新之后,加入堆中
            Q.push(Vex(v, mindist[v]));
        }
    }
    while(!Q.empty()){
        //取出到集合Vnew距离最小的点
        temp = Q.top();
        Q.pop();
        int addNode = temp.v;
        if(inTree[addNode]) continue;
        inTree[addNode] = true;
        res += mindist[addNode];
        //更新到集合Vnew的距离
        for(unsigned int i = 0; i < graph[addNode].size(); i++){
            int tempVex = graph[addNode][i].v;
            if(!inTree[tempVex] && mindist[tempVex] > graph[addNode][i].weight){
                mindist[tempVex] = graph[addNode][i].weight;
                Q.push(Vex(tempVex, mindist[tempVex]));
            }
        }
    }
    return res;
}

int main(){
    int n;
    int v1, v2, weight;
    while(scanf("%d", &n), n){
        Init(n);
        for(int i = 0; i < n*(n-1)/2; i++){
            scanf("%d%d%d", &v1, &v2, &weight);
            graph[v1].push_back(Vex(v2, weight));
            graph[v2].push_back(Vex(v1, weight));
        }
        printf("%d\n", Prim(1, n));
    }
    return 0;
}

(4)最短路bellman-ford

【未优化版】

#include <cstdio>
#include <cstring>
#define INF 0xfffffff
#define MAXN (100 + 10)
using namespace std;

struct edge{
    int from, to;
    edge(int f = 0, int t = 0)
    : from(f), to(t){}
};

edge es[MAXN*MAXN];
int cost[MAXN];
bool graph[MAXN][MAXN];
int d[MAXN];
//判断图是否联通
void Floyd(int n){
    for(int i = 1; i <= n; i++){
        for(int k = 1; k <= n; k++){
            for(int j = 1; j <= n; j++){
                if(!graph[i][j])
                    graph[i][j] = graph[i][k] && graph[k][j];
            }
        }
    }
}

bool bellman_ford(int s, int V, int E){
    for(int i = 0; i <= V; i++)
        d[i] = -INF;
    d[s] = 100;
    //重复对每一条边进行松弛操作
    for(int k = 0; k < V-1; k++){
        for(int i = 0; i < E; i++){
            edge e = es[i];
            //松弛操作
            if(d[e.to] < d[e.from] + cost[e.to] && d[e.from] + cost[e.to] > 0){
                d[e.to] = d[e.from] + cost[e.to];
            }
        }
    }
    //检查负权环
    for(int i = 0; i < E; i++){
        edge e = es[i];
        if(d[e.to] < d[e.from] + cost[e.to] && graph[e.to][V] && d[e.from] + cost[e.to] > 0)
            return true;
    }
    return d[V] > 0;
}

int main(){
    int n, m, cnt, vex;
    while(scanf("%d", &n), n != -1){
        memset(graph, false, sizeof(graph));
        cnt = 0;
        for(int i = 1; i <= n; i++){
            scanf("%d%d", &cost[i], &m);
            for(int j = 0; j < m; j++){
                scanf("%d", &vex);
                es[cnt++] = edge(i, vex);
                graph[i][vex] = true;
            }
        }
        Floyd(n);
        if(!graph[1][n] || !bellman_ford(1, n, cnt)){
            printf("hopeless\n");
        }
        else{
            printf("winnable\n");
        }
    }
    return 0;
}

【SPFA】

#include <cstdio>
#include <cstring>
#include <queue>
#define MAXN (100 + 10)
using namespace std;
//d表示s到各点的所经过路径的权值之和
//cost表示各点的权值
//cnt表示进入队列的次数
int d[MAXN], cost[MAXN], cnt[MAXN];
//reach表示两点之间是否联通,即可达
//graph记录两点之间是否有边
bool reach[MAXN][MAXN], graph[MAXN][MAXN];


void Init(){
    memset(d, 0, sizeof(d));
    memset(cnt, 0, sizeof(cnt));
    memset(graph, false, sizeof(graph));
    memset(reach, false, sizeof(reach));
}

//判断图是否联通
void Floyd(int n){
    for(int i = 1; i <= n; i++){
        for(int k = 1; k <= n; k++){
            for(int j = 1; j <= n; j++){
                if(!reach[i][j])
                    reach[i][j] = reach[i][k] && reach[k][j];
            }
        }
    }
}

bool SPFA(int s, int n){
    queue<int> Q;
    d[s] = 100;
    Q.push(s);
    while(!Q.empty()){
        int now = Q.front();
        Q.pop();
        cnt[now]++;
        //如果不存在负权环(PS:在本题中为正权环),即每个点进入队列的次数至多为n-1
        //若大于n-1,即表明必然存在负权环
        if(cnt[now] >= n) return reach[now][n];
        //依次枚举每条边
        for(int next = 1; next <= n; next++){
            if(graph[now][next] && d[now] + cost[next] > d[next] && d[now] + cost[next] > 0){
                Q.push(next);
                d[next] = d[now] + cost[next];
            }
        }
    }
    return d[n] > 0;
}

int main(){
    int n, m, vex;
    while(scanf("%d", &n), n != -1){
        Init();
        for(int i = 1; i <= n; i++){
            scanf("%d%d", &cost[i], &m);
            for(int j = 0; j < m; j++){
                scanf("%d", &vex);
                reach[i][vex] = true;
                graph[i][vex] = true;
            }
        }
        Floyd(n);
        if(!reach[1][n] || !SPFA(1, n)){
            printf("hopeless\n");
        }
        else{
            printf("winnable\n");
        }
    }
    return 0;
}

(5)最短路之Dijkstra

【未优化版】

#include <cstdio>
#include <vector>
#include <algorithm>
#define MAXN 200 + 10
#define INF 0xffffff
using namespace std;
struct Vex{
    int v, weight;
    Vex(int tv, int tw):v(tv), weight(tw){}
};
//graph用来记录图的信息
vector<Vex> graph[MAXN];
//判断是否已经找到最短路
bool inTree[MAXN];
//源点s到各顶点最短路的值
int mindist[MAXN];
//初始化
void Init(int n){
    for(int i = 0; i < n; i++){
        inTree[i] = false;
        graph[i].clear();
        mindist[i] = INF;
    }
}
//s表示源点,t表示终点,n表示顶点数目
int Dijkstra(int s, int t, int n){
    int tempMin, tempVex, addNode;
    //初始化s
    mindist[s] = 0;
    //将源点s标记为访问过
    inTree[s] = true;
    //题目中可能有重边,我们去除重边
    for(unsigned int i = 0; i < graph[s].size(); i++)
        mindist[graph[s][i].v] = min(mindist[graph[s][i].v], graph[s][i].weight);
    //从剩下的n-1个点逐个枚举
    for(int nNode = 1; nNode <= n-1; nNode++){
        tempMin = INF;
        //寻找所有未访问过点中,有最小距离的点
        for(int i = 0; i < n; i++){
            if(!inTree[i] && mindist[i] < tempMin){
                tempMin = mindist[i];
                addNode = i;
            }
        }
        //将该点标记为访问过
        inTree[addNode] = true;
        //将与该点相邻的点进行松弛操作
        for(unsigned int i = 0; i < graph[addNode].size(); i++){
            tempVex = graph[addNode][i].v;
            if(!inTree[tempVex] && tempMin + graph[addNode][i].weight < mindist[tempVex]){
                mindist[tempVex] = tempMin + graph[addNode][i].weight;
            }
        }
    }
    return mindist[t];
}


int main(){
    int n, m;
    int v1, v2, x, s, t;
    while(scanf("%d%d", &n, &m) != EOF){
        Init(n);
        for(int i = 0; i < m; i++){
            scanf("%d%d%d", &v1, &v2, &x);
            graph[v1].push_back(Vex(v2, x));
            graph[v2].push_back(Vex(v1, x));
        }
        scanf("%d%d", &s, &t);
        int ans = Dijkstra(s, t, n);
        if(ans == INF)
            printf("-1\n");
        else
            printf("%d\n", ans);
    }
    return 0;
}


【堆优化版】

#include <cstdio>
#include <vector>
#include <queue>
#include <algorithm>
#define MAXN 200 + 10
#define INF 0xffffff
using namespace std;
struct edge{
    int to, cost;
};
typedef pair<int, int> P;
vector<edge> graph[MAXN];
int mindist[MAXN];

void Init(int n){
    for(int i = 0; i < n; i++){
        graph[i].clear();
        mindist[i] = INF;
    }
}

int Dijkstra_heap(int s, int t, int n){
    //pair的first存放s->v的距离
    //second存放顶点v
    priority_queue<P, vector<P>, greater<P> > Q;
    //初始化源点s的信息
    mindist[s] = 0;
    Q.push(P(0, s));
    while(!Q.empty()){
        //每次从堆中取出最小值
        P p = Q.top(); Q.pop();
        int v = p.second;
        //当取出的值不是当前最短距离的话,就丢弃这个值
        if(mindist[v] < p.first) continue;
        //将与其相邻的点,进行松弛操作
        for(unsigned int i = 0; i < graph[v].size(); i++){
            edge e = graph[v][i];
            if(mindist[e.to] > mindist[v] + e.cost){
                mindist[e.to] = mindist[v] + e.cost;
                //将满足条件的点重新加入堆中
                Q.push(P(mindist[e.to], e.to));
            }
        }
    }
    return mindist[t];
}


int main()
{
    int n, m;
    int v1, v2, x, s, t;
    while(scanf("%d%d", &n, &m) != EOF){
        Init(n);
        for(int i = 0; i < m; i++){
            scanf("%d%d%d", &v1, &v2, &x);
            graph[v1].push_back({v2, x});
            graph[v2].push_back({v1, x});
        }
        scanf("%d%d", &s, &t);
        int ans = Dijkstra_heap(s, t, n);
        if(ans == INF)
            printf("-1\n");
        else
            printf("%d\n", ans);
    }
    return 0;
}

【次短路】

#include <cstdio>
#include <cstring>
#include <queue>
#include <algorithm>
#define MAXN (5000 + 10)
#define INF (5000*5000*2)
using namespace std;

struct edge{
    int to, cost;
    edge(int tv = 0, int tc = 0):
        to(tv), cost(tc){}
};
typedef pair<int ,int> P;
int N, R;
vector<edge> graph[MAXN];
int dist[MAXN];     //最短距离
int dist2[MAXN];    //次短距离

void solve(){
    fill(dist, dist+N, INF);
    fill(dist2, dist2+N, INF);
    //从小到大的优先队列
    //使用pair而不用edge结构体
    //是因为这样我们不需要重载运算符
    //pair是以first为主关键字进行排序
    priority_queue<P, vector<P>, greater<P> > Q;
    //初始化源点信息
    dist[0] = 0;
    Q.push(P(0, 0));
    //同时求解最短路和次短路
    while(!Q.empty()){
        P p = Q.top(); Q.pop();
        //first为s->to的距离,second为edge结构体的to
        int v = p.second, d = p.first;
        //当取出的值不是当前最短距离或次短距离,就舍弃他
        if(dist2[v] < d) continue;
        for(unsigned i = 0; i < graph[v].size(); i++){
            edge &e = graph[v][i];
            int d2 = d + e.cost;
            if(dist[e.to] > d2){
                swap(dist[e.to], d2);
                Q.push(P(dist[e.to], e.to));
            }
            if(dist2[e.to] > d2 && dist[v] < d2){
                dist2[e.to] = d2;
                Q.push(P(dist2[e.to], e.to));
            }
        }
    }
    printf("%d\n", dist2[N-1]);
}

int main(){
    int A, B, D;
    scanf("%d%d", &N, &R);
    for(int i = 0; i < R; i++){
        scanf("%d%d%d", &A, &B, &D);
        graph[A-1].push_back(edge(B-1, D));
        graph[B-1].push_back(edge(A-1, D));
    }
    solve();
    return 0;
}


(6)割点与桥(割边)

#include <cstdio>
#include <vector>
#include <cstring>
#include <iostream>
#define MAXN 100
using namespace std;
int low[MAXN], iscut[MAXN];
int pre[MAXN];
vector<int> graph[MAXN];
int dfs_clock;

void init(){
    dfs_clock = 0;
    memset(pre, 0, sizeof(pre));
    memset(low, 0, sizeof(low));
    memset(iscut, 0, sizeof(iscut));
    for(int i = 0; i < MAXN; i++){
        graph[i].clear();
    }
}

void add_edge(int from, int to){
    graph[from].push_back(to);
    graph[to].push_back(from);
}

void dfs(int u, int fa){
    low[u] = pre[u] = ++dfs_clock;
    int child = 0;
    for(int i = 0; i < graph[u].size(); i++){
        int v = graph[u][i];
        if(!pre[v]){
            child++;
            dfs(v, u);
            low[u] = min(low[u], low[v]);
            //if(low[v] > pre[u]) 则(u, v)为桥(割边)
            if(low[v] >= pre[u]) iscut[u] = 1;
        }
        else if(pre[v] < pre[u] && v != fa)
            low[u] = min(low[u], pre[v]);
    }
    //如果搜索树只有一个儿子节点,那么根肯定不是割点
    if(fa < 0 && child == 0) iscut[u] = 0;
}

int main(){
    int n;
    while(scanf("%d", &n), n){
        init();
        int v, u;
        while(scanf("%d", &u), u){
            while(getchar() != '\n'){
                scanf("%d", &v);
                add_edge(u-1, v-1);
            }
        }
        dfs(0, -1);
        int ans = 0;
        for(int i = 0; i < n; i++){
            if(iscut[i]) ans++;
        }
        printf("%d\n", ans);
    }
    return 0;
}

(7)强连通分量

【Tarjan】

#include <cstdio>
#include <vector>
#include <stack>
#include <cstring>
#include <queue>
#include <iostream>
#define MAXN (100000 + 10)
using namespace std;

vector<int> graph[MAXN], newG[MAXN];
int pre[MAXN], low[MAXN], sccno[MAXN], scc_cnt, dfs_clock;
stack<int> s;
int vis[MAXN], dist[MAXN];
const int INF = 0xfffffff;

void init(){
    scc_cnt = 0;
    dfs_clock = 0;
    fill(dist, dist+MAXN,INF);
    memset(vis, 0, sizeof(vis));
    memset(pre, 0, sizeof(pre));
    memset(low, 0, sizeof(low));
    memset(sccno, 0, sizeof(sccno));
    while(!s.empty()) s.pop();
    for(int i = 0; i < MAXN; i++){
        graph[i].clear();
        newG[i].clear();
    }
}
void dfs(int u){
    pre[u] = low[u] = ++dfs_clock;
    s.push(u);
    for(int i = 0; i < graph[u].size(); i++){
        int v = graph[u][i];
        if(!pre[v]){
            dfs(v);
            low[u] = min(low[v], low[u]);
        }
        else if(!sccno[v]){
            low[u] = min(low[u], pre[v]);
        }
    }
    if(low[u] == pre[u]){
        while(1){
            int x = s.top(); s.pop();
            sccno[x] = scc_cnt;
            if(x == u) break;
        }
        scc_cnt++;
    }
}

void find_scc(int n){
    for(int i = 0; i < n; i++){
        if(!pre[i]) dfs(i);
    }
}
void solve(int n) {
    find_scc(n);
    for(int i = 0; i < n; i++){//利用强连通分量,重新建图
        for(int j = 0; j < graph[i].size(); j++){
            int v = graph[i][j];
            if(sccno[i] != sccno[v]){
                newG[sccno[i]].push_back(sccno[v]);
            }
        }
    }
    queue<int> Q;
    Q.push(sccno[0]);
    dist[sccno[0]] = 0;
    while(!Q.empty()){
        int u = Q.front(); Q.pop();
        vis[u] = 1;
        for(int i = 0; i < newG[u].size(); i++){
            int v = newG[u][i];
            if(!vis[v]){
                dist[v] = min(dist[v], dist[u] + 1);
                Q.push(v);
            }
        }
    }
    int ans = dist[sccno[n-1]];
    if(ans == INF) ans = -1;
    printf("%d\n", ans);
}
int main(){
    int T, n, m, a, b;
    scanf("%d", &T);
    while(T-- && scanf("%d%d", &n, &m)){
        init();
        for(int i = 0; i < m; i++){
            scanf("%d%d", &a, &b);
            graph[a].push_back(b);
        }
        solve(n);
    }
    return 0;
}

【Kosaraju】

#include <cstdio>
#include <algorithm>
#include <vector>
#include <cstring>
#define MAXN (10000 + 10)
#define MAX_M (50000 + 10)
using namespace std;
int V; // 顶点数
vector<int> G[MAXN];   // 图的邻接表表示
vector<int> rG[MAXN];  // 把边反向后的图
vector<int> vs;         // 后序遍历顺序的顶点列表
bool used[MAXN];       // 访问标记
int cmp[MAXN];         // 所属强连通分量的拓扑序

void add_edge(int from, int to){
    G[from].push_back(to);
    rG[to].push_back(from);
}

void dfs(int v){
    used[v] = true;
    for(unsigned i = 0; i < G[v].size(); i++){
        if(!used[G[v][i]]) dfs(G[v][i]);
    }
    vs.push_back(v);
}

void rdfs(int v, int k){
    used[v] = true;
    cmp[v] = k;
    for(unsigned i = 0; i < rG[v].size(); i++){
        if(!used[rG[v][i]]) rdfs(rG[v][i], k);
    }
}

int scc(){
    fill(used, used+MAXN, false);
    vs.clear();
    for(int i = 0; i < V; i++){
        if(!used[i]) dfs(i);
    }
    fill(used, used+MAXN, false);
    int k = 0;
    for(int i = vs.size()-1; i >= 0; i--){
        if(!used[vs[i]]) rdfs(vs[i], k++);
    }
    return k;
}
// 输入
int N, M;

void solve() {
    V = N;
    int n = scc();
    int num = 0, u = 0;
    for(int i = 0; i < V; i++){
        if(cmp[i] == n-1){
            u = i;
            num++;
        }
    }
    memset(used, 0, sizeof(used));
    rdfs(u, 0);
    for(int i = 0; i < V; i++){
        if(!used[i]){
            num = 0;
            break;
        }
    }
    printf("%d\n", num);
}
int main(){
    int a, b;
    scanf("%d%d", &N, &M);
    for(int i = 0; i < M; i++){
        scanf("%d%d", &a, &b);
        add_edge(a-1, b-1);
    }
    solve();
    return 0;
}



3、数学

(1)快速幂

typedef long long ll; //注意这里不一定都是long long 有时 int 也行
ll mod_pow(ll x, ll n, ll mod){
    ll res = 1;
    while( n > 0 ){ 
        if( n & 1 ) res = res * x % mod;    //n&1其实在这里和 n%2表达的是一个意思
        x = x * x % mod;
        n >>= 1;                 //n >>= 1这个和 n/=2表达的是一个意思
    }
    return res;
}

(2)矩阵快速幂

#include <limits.h>
class Matrix{
public:
    int **m; //保存矩阵值的指针
    int row, col, mod; //分别保存矩阵的行、列以及取模的值
    Matrix(int r = 0, int c = 0, int d = INT_MAX);
    Matrix(const Matrix &value);
    ~Matrix();
    Matrix operator + (const Matrix &rht) const;
    Matrix operator * (const Matrix &rht) const;
    Matrix& operator = (const Matrix &rht);
    Matrix pow(int n) const;
};

Matrix::Matrix(int r, int c, int d):row(r), col(c), mod(d){
    m = new int*[row];
    for(int i = 0; i < row; i++){
        m[i] = new int[col];
    }
    for(int i = 0; i < row; i++){
        for(int j = 0; j < col; j++){
            m[i][j] = 0;
        }
    }
}
Matrix::Matrix(const Matrix &value){
    row = value.row;
    col = value.col;
    mod = value.mod;
    m = new int*[row];
    for(int i = 0; i < row; i++){
        m[i] = new int[col];
    }
    for(int i = 0; i < row; i++){
        for(int j = 0; j < col; j++){
            m[i][j] = value.m[i][j];
        }
    }
}
Matrix::~Matrix(){
    for(int i = 0; i < row; i++){
        delete[] m[i];
    }
    delete[] m;
}
Matrix Matrix::operator + (const Matrix &rht) const{
    Matrix temp(row, col, mod);
    for(int i = 0; i < row; i++){
        for(int j = 0; j < col; j++){
            temp.m[i][j] = (m[i][j] + rht.m[i][j])%mod;
        }
    }
    return temp;
}
Matrix Matrix::operator * (const Matrix &rht) const{
    Matrix temp(row, rht.col, mod);
    for(int i = 0; i < row; i++){
        for(int k = 0; k < rht.row; k++){
            for(int j = 0; j < rht.col; j++){
                temp.m[i][j] = (temp.m[i][j] + m[i][k]*rht.m[k][j])%mod;
            }
        }
    }
    return temp;
}
Matrix& Matrix::operator = (const Matrix &rht){
    for(int i = 0; i < row; i++){
        for(int j = 0; j < col; j++){
            m[i][j] = rht.m[i][j];
        }
    }
    return *this;
}
//矩阵快速幂
Matrix Matrix::pow(int n) const{
    Matrix a(*this), res(row, col, mod);
    //将矩阵res初始化为单位矩阵
    for(int i = 0; i < row; i++){
        res.m[i][i] = 1;
    }
    //这时候直接使用快速幂的代码
    while(n > 0){
        if(n & 1) res = res * a;
        a = a * a;
        n >>= 1;
    }
    return res;
}

(3)倍增法

//Matrix 类,同矩阵快速幂的Matrix类

//倍增法求解a^1 + a^2 + ... + a^n
Matrix slove(const Matrix &a, int n){
    //递归终点
    if(n == 1) return a;
    //temp 递归表示a^1 + a^2 + ... + a^(n/2)
    Matrix temp = slove(a, n/2);
    //sum 表示 a^1 + a^2 + ... + a^(n/2) + (a^(n/2))*(a^1 + a^2 + ... + a^(n/2))
    Matrix sum = temp + temp*a.pow(n/2);
    //如果当n为奇数,我们会发现我们的(n/2 + n/2) == n-1
    //于是我们需要补上一项: a^n
    if(n & 1) sum = sum + a.pow(n);
    return sum;
}


猜你喜欢

转载自blog.csdn.net/luomingjun12315/article/details/51254388
今日推荐