• 模板整理 (施工中 2017.8.30更新)


    目录

    数据结构

        1 字典树

    字符串

        1 Manacher最长回文子串

        2 KMP

        3 扩展KMP

        4 AC自动机

    图论

        1 网络流dinic

        2 zkw费用流

        3 有向图的强联通分量

        4 无向图的强联通分量

        5 匈牙利匹配

        6 最小生成树

        7 最短路

        8 欧拉回路Fleury

    数论

        1 中国剩余定理

        2 大质数判定

        3 约瑟夫环

    博弈

        1 Bash博弈

        2 威佐夫博弈

        3 Nim博弈

        4 斐波那契博弈

        5 Anti-num博弈

        6 SG函数

    其他

        1 祖传头文件

        2 大数

        3 STL

        4 正常的读入挂

        5 fread读入挂

        6 莫队算法

    数据结构

    1.字典树

    使用时记得先初始化第一个点

    const int MAXN = 1e2 + 5;
    int Maxlen, Maxnum;
    int T, K;
    
    struct node {
        int next[27];
        int v, num;
        void init() {
            v=-1;
            num = 0;
            memset(next,-1,sizeof(next));
        }
    };
    struct node L[1000000];
    int tot=0;
    
    void add(char a[]) {
        int now=0;
        int len = strlen(a);
        for(int i=0; i<len; i++) {
            int tmp=a[i]-'a';
            int next=L[now].next[tmp];
            if(next==-1) {
                next=++tot;
                L[next].init();
                L[now].next[tmp]=next;
            }
            now=next;
            L[now].num ++;
            if(L[now].num >= 3) {
                if(Maxlen < i + 1) {
                    Maxlen = i + 1;
                    Maxnum = L[now].num;
                } else if(Maxnum < L[now].num && Maxlen <= i + 1) {
                    Maxnum = L[now].num;
                }
            }
        }
        L[now].v=0;
    }
    
    int query(char s2[]) {
        int len = strlen(s2);
        int now = 0;
        for(int i = 0; i < len; i++) {
            int temp = s2[i] - 'a';
            int next = L[now].next[temp];
            if(next == -1) return 0;
            now = next;
        }
        return L[now].num;
    }
    
    int main()
    {
        L[0].init();
    
        return 0;
    }
    

     

    字符串

    1 Manacher最长回文子串

    const int MAX = 200000 + 10;
    char s[MAX * 2];//记得要开两倍
    int p[MAX * 2];
    int manacher(char *s) {
        int len = strlen(s), id = 0, ans = 0;
        for(int i = len; i >= 0; i--) {
            s[i + i + 2] = s[i];
            s[i + i + 1] = '#';
        }
        s[0] = '*';//防越界,很重要!!
        for(int i = 2; i < 2 * len + 1; ++i) {
            if(p[id] + id > i) p[i] = min(p[2 * id - i], p[id] + id - i);
            else p[i] = 1;
            while(s[i - p[i]] == s[i + p[i]]) p[i]++;
            if(id + p[id] < i + p[i]) id = i;
            ans = max(ans, p[i] - 1);
        }
        return ans;
    }
    

      

     2.KMP

    const int MX = 2000 + 5;
    
    /*
    int Next[MX], n;
    void GetNext() {
        Next[0] = 0;
        for(int i = 1; i < n; i++) {
            int j = Next[i - 1];
            while(j && S[i] != S[j]) j = Next[j - 1];
            Next[i] = S[i] == S[j] ? j + 1 : 0;
        }
    }
    
    /*求前缀i 循环节最长长度
    int GetCir(int p) {
        return (p + 1) % (p - Next[p] + 1) == 0 ? p - Next[p] + 1 : p + 1;
    }
    
    */
    
    /*会有重叠部分*/
    int Next[MX];
    int KMP(char *A, char *B) {
        int m = strlen(A), n = strlen(B);
        Next[0] = 0;
        for(int i = 1; i < n; i++) {
            int k = Next[i - 1];
            while(B[i] != B[k] && k) k = Next[k - 1];
            Next[i] = B[i] == B[k] ? k + 1 : 0;
        }
        int ans = 0, j = 0;
        for(int i = 0; i < m; i++) {
            while(A[i] != B[j] && j) j = Next[j - 1];
            if(A[i] == B[j]) j++;
            if(j == n) ans++;
        }
        return ans;
    }
    

     

    3.扩展KMP

    /*
    * 扩展KMP算法
    */
    //next[i]:x[i...m-1]与x[0...m-1]的最长公共前缀
    //extend[i]:y[i...n-1]与x[0...m-1]的最长公共前缀
    void pre_EKMP(char x[], int m, int next[]) {
        next[0] = m;
        int j = 0;
        while(j + 1 < m && x[j] == x[j + 1])j++;
        next[1] = j;
        int k = 1;
        for(int i = 2; i < m; i++) {
            int p = next[k] + k - 1;
            int L = next[i - k];
            if(i + L < p + 1)next[i] = L;
            else {
                j = max(0, p - i + 1);
                while(i + j < m && x[i + j] == x[j])j++;
                next[i] = j;
                k = i;
            }
        }
    }
    void EKMP(char x[], int m, char y[], int n, int next[], int extend[]) {
        pre_EKMP(x, m, next);
        int j = 0;
        while(j < n && j < m && x[j] == y[j])j++;
        extend[0] = j;
        int k = 0;
        for(int i = 1; i < n; i++) {
            int p = extend[k] + k - 1;
            int L = next[i - k];
            if(i + L < p + 1)extend[i] = L;
            else {
                j = max(0, p - i + 1);
                while(i + j < n && j < m && y[i + j] == x[j])j++;
                extend[i] = j;
                k = i;
            }
        }
    }
    

       

    4.AC自动机

    struct Trie {
        int next[500010][26], fail[500010], end[500010];
        int root, L;
        int newnode() {
            for(int i = 0; i < 26; i++)
                next[L][i] = -1;
            end[L++] = 0;
            return L - 1;
        }
        void init() {
            L = 0;
            root = newnode();
        }
        void insert(char buf[]) {
            int len = strlen(buf);
            int now = root;
            for(int i = 0; i < len; i++) {
                if(next[now][buf[i] - 'a'] == -1)
                    next[now][buf[i] - 'a'] = newnode();
                now = next[now][buf[i] - 'a'];
            }
            end[now]++;
        }
        void build() {
            queue<int>Q;
            fail[root] = root;
            for(int i = 0; i < 26; i++)
                if(next[root][i] == -1)
                    next[root][i] = root;
                else {
                    fail[next[root][i]] = root;
                    Q.push(next[root][i]);
                }
            while( !Q.empty() ) {
                int now = Q.front();
                Q.pop();
                for(int i = 0; i < 26; i++)
                    if(next[now][i] == -1)
                        next[now][i] = next[fail[now]][i];
                    else {
                        fail[next[now][i]] = next[fail[now]][i];
                        Q.push(next[now][i]);
                    }
            }
        }
        int query(char buf[]) {
            int len = strlen(buf);
            int now = root;
            int res = 0;
            for(int i = 0; i < len; i++) {
                now = next[now][buf[i] - 'a'];
                int temp = now;
                while( temp != root ) {
                    res += end[temp];
                    end[temp] = 0;
                    temp = fail[temp];
                }
            }
            return res;
        }
        void debug() {
            for(int i = 0; i < L; i++) {
                printf("id = %3d,fail = %3d,end = %3d,chi = [", i, fail[i], end[i]);
                for(int j = 0; j < 26; j++)
                    printf("%2d", next[i][j]);
                printf("]
    ");
            }
        }
    };
    
    Trie AC;
    

      

    图论 

    1.网络流Dinic

    const int maxn = 405;    //开四倍
    const int maxe = 4*maxn*maxn;  
    const int inf = 0x3f3f3f3f;  
    struct MaxFlow {  
        struct Edge {  
            int v, w, nxt;  
        } edge[maxe];  
        int head[maxn], tot, level[maxn];  
        void init(){  
            memset(head,-1,sizeof(head));  
            tot=0;  
        }  
        void add(int u, int v, int w) {  
            edge[tot].v = v;  
            edge[tot].w = w;  
            edge[tot].nxt = head[u];  
            head[u] = tot++;  
      
            edge[tot].v = u;  
            edge[tot].w = 0;  
            edge[tot].nxt = head[v];  
            head[v] = tot++;  
        }  
        bool bfs(int s, int t) {  
            memset(level, -1, sizeof(level));  
            queue<int>q;  
            q.push(s);  
            level[s] = 0;  
            while(!q.empty()) {  
                int u = q.front(); q.pop();  
                for(int i = head[u]; ~i; i = edge[i].nxt) {  
                    if(edge[i].w > 0 && level[edge[i].v] < 0) {  
                        level[edge[i].v] = level[u] + 1;  
                        q.push(edge[i].v);  
                    }  
                }  
            }  
            return level[t] > 0;  
        }  
        int dfs(int u, int t, int f) {  
            if(u == t) return f;  
            for(int i = head[u]; ~i; i = edge[i].nxt) {  
                int v = edge[i].v;  
                if(edge[i].w > 0 && level[v] > level[u]) {  
                    int d = dfs(v, t, min(f, edge[i].w));  
                    if(d > 0) {  
                        edge[i].w -= d;  
                        edge[i ^ 1].w += d;  
                        return d;  
                    }  
                }  
            }  
            level[u] = -1; //不太确定,如果WA了把这句删掉试试  
            return 0;  
        }  
        int solve(int s, int t) {  
            int flow = 0, f;  
            while(bfs(s, t)) {  
      
                while(f = dfs(s, t, inf)) flow += f;  
      
            }  
            return flow;  
        }  
    }F;  
    

    2.

    zkw费用流

    const int MX = 80000;
    const int MXE = 4 * MX * MX;
    const int ME = 4e5 + 5;//边的数量
    
    struct MCMF {
        int S, T;//源点,汇点
        int tot, n;
        int st, en, maxflow, mincost;
        bool vis[MX];
        int head[MX], cur[MX], dis[MX];
        int roade[MX], roadv[MX], rsz; //用于打印路径
    
        queue <int> Q;
        struct Edge {
            int v, cap, cost, nxt, flow;
            Edge() {}
            Edge(int a, int b, int c, int d) {
                v = a, cap = b, cost = c, nxt = d, flow = 0;
            }
        } E[ME], SE[ME];
    
        void init(int _n) {
            n = _n, tot = 0;
            for(int i = 0; i <= n; i++) head[i] = -1;
        }
        void edge_add(int u, int v, int cap, int cost) {
            E[tot] = Edge(v, cap, cost, head[u]);
            head[u] = tot++;
            E[tot] = Edge(u, 0, -cost, head[v]);
            head[v] = tot++;
        }
        bool adjust() {
            int v, min = INF;
            for(int i = 0; i <= n; i++) {
                if(!vis[i]) continue;
                for(int j = head[i]; ~j; j = E[j].nxt) {
                    v = E[j].v;
                    if(E[j].cap - E[j].flow) {
                        if(!vis[v] && dis[v] - dis[i] + E[j].cost < min) {
                            min = dis[v] - dis[i] + E[j].cost;
                        }
                    }
                }
            }
            if(min == INF) return false;
            for(int i = 0; i <= n; i++) {
                if(vis[i]) {
                    cur[i] = head[i];
                    vis[i] = false;
                    dis[i] += min;
                }
            }
            return true;
        }
        int augment(int i, int flow) {
            if(i == en) {
                mincost += dis[st] * flow;
                maxflow += flow;
                return flow;
            }
            vis[i] = true;
            for(int j = cur[i]; j != -1; j = E[j].nxt) {
                int v = E[j].v;
                if(E[j].cap == E[j].flow) continue;
                if(vis[v] || dis[v] + E[j].cost != dis[i]) continue;
                int delta = augment(v, std::min(flow, E[j].cap - E[j].flow));
                if(delta) {
                    E[j].flow += delta;
                    E[j ^ 1].flow -= delta;
                    cur[i] = j;
                    return delta;
                }
            }
            return 0;
        }
        void spfa() {
            int u, v;
            for(int i = 0; i <= n; i++) {
                vis[i] = false;
                dis[i] = INF;
            }
            Q.push(st);
            dis[st] = 0; vis[st] = true;
            while(!Q.empty()) {
                u = Q.front(), Q.pop(); vis[u] = false;
                for(int i = head[u]; ~i; i = E[i].nxt) {
                    v = E[i].v;
                    if(E[i].cap == E[i].flow || dis[v] <= dis[u] + E[i].cost) continue;
                    dis[v] = dis[u] + E[i].cost;
                    if(!vis[v]) {
                        vis[v] = true;
                        Q.push(v);
                    }
                }
            }
            for(int i = 0; i <= n; i++) {
                dis[i] = dis[en] - dis[i];
            }
        }
        int zkw(int s, int t) {
            st = s, en = t;
            spfa();
            mincost = maxflow = 0;
            for(int i = 0; i <= n; i++) {
                vis[i] = false;
                cur[i] = head[i];
            }
            do {
                while(augment(st, INF)) {
                    memset(vis, false, n * sizeof(bool));
                }
            } while(adjust());
            return mincost;
        }
    } M;

    3.有向图的强联通分量缩点

    const int MX = 5e3 + 5;
    const int INF = 0x3f3f3f3f;
    struct Edge {
        int u, v, nxt;
    } E[60005];
    int Head[MX], erear;
    void edge_init() {
        erear = 0;
        memset(Head, -1, sizeof(Head));
    }
        void edge_add(int u, int v) {
        E[erear].u = u;
        E[erear].v = v;
        E[erear].nxt = Head[u];
        Head[u] = erear++;
    }
    int n, m, IN[MX], cnt[MX], val[MX];
    int bsz, ssz, dsz;
    int Low[MX], DFN[MX];
    int belong[MX], Stack[MX];
    bool inStack[MX];
    void Init_tarjan(int n) {
        bsz = ssz = dsz = 0;
        for(int i = 1; i <= n; ++i) Low[i] = DFN[i] = 0;
    }
    void Tarjan(int u) {
        Stack[++ssz] = u;
        inStack[u] = 1;
        Low[u] = DFN[u] = ++dsz;
        for(int i = Head[u]; ~i; i = E[i].nxt) {
            int v = E[i].v;
            if(!DFN[v]) {
                Tarjan(v);
                Low[u] = min( Low[v], Low[u]);
            } else if(inStack[v]) {
                Low[u] = min( Low[u], DFN[v]);
            }
        }
        if(Low[u] == DFN[u]) {
            ++bsz;
            int v;
            do {
                v = Stack[ssz--];
                inStack[v] = 0;
                belong[v] = bsz;
            } while(u != v);
        }
    }
    int solve(int n) {
        Init_tarjan(n);
        for (int i = 1; i <= n; i++) {
            if (!DFN[i]) Tarjan(i);
        } edge_init();
        for(int i = 0; i < m; i++) {
            int u = E[i].u, v = E[i].v;
            u = belong[u]; v = belong[v];
            if(u != v) {
                edge_add(u, v);
            }
        }
    }
    

      

    4.无向图的强联通分量缩点

    const int MX = 1e5 + 10;
    
    struct Edge {
        int u, v, nxt;
    } E[MX];
    int Head[MX], erear;
    void edge_init() {
        erear = 0;
        memset(Head, -1, sizeof(Head));
    }
    void edge_add(int u, int v) {
        E[erear].u = u;
        E[erear].v = v;
        E[erear].nxt = Head[u];
        Head[u] = erear++;
    }
    int n, m, IN[MX], cnt[MX], val[MX];
    int bsz, ssz, dsz;
    int Low[MX], DFN[MX];
    void Init_tarjan(int n) {
        bsz = ssz = dsz = 0;
        for(int i = 1; i <= n; ++i) Low[i] = DFN[i] = 0;
    }
    
    int Stack[MX], inStack[MX], Belong[MX];
    void trajan(int u, int e) {
        inStack[u] = 1;
        Stack[++ssz] = u;
        DFN[u] = Low[u] = ++dsz;
        for(int i = Head[u]; ~i; i = E[i].nxt) {
            int v = E[i].v;
            if((i ^ 1) == e) continue;
            if(!DFN[v]) {
                trajan(v, i);
                Low[u] = min(Low[u], Low[v]);
            } else if(inStack[v]) {
                Low[u] = min(Low[u], Low[v]);
            }
        }
        if(DFN[u] == Low[u]) {
            bsz++; int v;
            do {
                v = Stack[ssz--];
                inStack[v] = 0;
                Belong[v] = bsz;
            } while(ssz && v != u);
        }
    }
    void tarjan_solve(int n) {
        dsz = bsz = ssz = 0;
        memset(DFN, 0, sizeof(DFN));
        for(int i = 1; i <= n; i++) {
            if(!DFN[i]) trajan(i, -1);
        }
        /*缩点*/
        edge_init();
        for(int i = 0; i < 2 * m; i += 2) {
            int u = E[i].u, v = E[i].v;
            u = Belong[u]; v = Belong[v];
            if(u == v) continue;
            edge_add(u, v);
            edge_add(v, u);
        }
    }
    

      

    5.匈牙利匹配

    /*复杂度O(VE)
    最小点覆盖=最大匹配数
    最小边覆盖=左右点数-最大匹配数
    最小路径覆盖=点数-最大匹配数
    最大独立集=点数-最大匹配数
    */
    
    const int MX = 205;
    
    struct Edge {
        int v, nxt;
    }E[MX];
    
    int Head[MX], tot;
    int match[MX];
    bool vis[MX];
    
    void edge_init() {
        memset(Head, -1, sizeof(Head));
        memset(match, 0, sizeof(match));
        tot = 0;
    }
    
    void edge_add(int u, int v) {
        E[tot].v = v;
        E[tot].nxt = Head[u];
        Head[u] = tot++;
    }
    
    bool DFS(int u) {
        for(int i = Head[u]; ~i; i = E[i].nxt) {
            int v = E[i].v;
            if(!vis[v]) {
                vis[v] = 1;
                if(match[v] == -1 || DFS(match[v])) {
                    //如果v已经被匹配了,且被匹配的点不是
                    //当前点u,那么就去修改这个match[v],如果修改成功了,那么就可以返回1
                    match[v] = u;
                    return 1;
                }
            }
        }
        return 0;
    }
    int BM(int n) {
        int res = 0;
        memset(match, -1, sizeof(match));
        for(int u = 1; u <= n; u++) {
            memset(vis, 0, sizeof(vis));
            if(DFS(u)) res++;//找与u匹配的点v,如果找到了答案就+1
        }
        return res;
    }
    

      

    6.最小生成树

    /*
    * Kruskal算法求MST
    */
    const int MAXN = 1000 + 5; //最大点数
    const int MAXM = 50000 + 5; //最大边数
    int F[MAXN];//并查集使用
    struct Edge {
        int u, v, w;
    } edge[MAXM]; //存储边的信息,包括起点/终点/权值
    int tol;//边数,加边前赋值为0
    void addedge(int u, int v, int w) {
        edge[tol].u = u;
        edge[tol].v = v;
        edge[tol++].w = w;
    }
    bool cmp(Edge a, Edge b) {
        //排序函数,讲边按照权值从小到大排序
        return a.w < b.w;
    }
    int find(int x) {
        if(F[x] == -1)return x;
        else return F[x] = find(F[x]);
    }
    int Kruskal(int n) { //传入点数,返回最小生成树的权值,如果不连通返回-1
        memset(F, -1, sizeof(F));
        sort(edge, edge + tol, cmp);
        int cnt = 0; //计算加入的边数
        int ans = 0;
        for(int i = 0; i < tol; i++) {
            int u = edge[i].u;
            int v = edge[i].v;
            int w = edge[i].w;
            int t1 = find(u);
            int t2 = find(v);
            if(t1 != t2) {
                ans += w;
                F[t1] = t2;
                cnt++;
            }
            if(cnt == n - 1)break;
        }
        if(cnt < n - 1)return -1; //不连通
        else return ans;
    }
    
    /*
    * Prim求MST
    * 耗费矩阵cost[][],标号从0开始,0~n-1
    * 返回最小生成树的权值,返回-1表示原图不连通
    */
    const int INF = 0x3f3f3f3f;
    const int MAXN = 110;
    bool vis[MAXN];
    int lowc[MAXN];
    int Prim(int cost[][MAXN], int n) { //点是0~n-1
        int ans = 0;
        memset(vis, false, sizeof(vis));
        vis[0] = true;
        for(int i = 1; i < n; i++)lowc[i] = cost[0][i];
        for(int i = 1; i < n; i++) {
            int minc = INF;
            int p = -1;
            for(int j = 0; j < n; j++)
                if(!vis[j] && minc > lowc[j]) {
                    minc = lowc[j];
                    p = j;
                }
            if(minc == INF)return -1; //原图不连通
            ans += minc;
            vis[p] = true;
            for(int j = 0; j < n; j++)
                if(!vis[j] && lowc[j] > cost[p][j])
                    lowc[j] = cost[p][j];
        }
        return ans;
    }
    

       

     7.最短路

    1、最短路
    1.1 Dijkstra单源最短路,邻接矩阵形式 
    权值必须是非负
    /*
    * 单源最短路径,Dijkstra算法,邻接矩阵形式,复杂度为O(n^2)
    * 求出源beg到所有点的最短路径,传入图的顶点数,和邻接矩阵cost[][]
    * 返回各点的最短路径lowcost[], 路径pre[].pre[i]记录beg到i路径上的父结点,pre[beg]=-1
    * 可更改路径权类型,但是权值必须为非负
    *
    */
    const int MAXN = 1010;
    #define typec int
    const typec INF = 0x3f3f3f3f; //防止后面溢出,这个不能太大
    bool vis[MAXN];
    int pre[MAXN];
    void Dijkstra(typec cost[][MAXN], typec lowcost[], int n, int beg) {
        for(int i = 0; i < n; i++) {
            lowcost[i] = INF; vis[i] = false; pre[i] = -1;
        }
        lowcost[beg] = 0;
        for(int j = 0; j < n; j++) {
            int k = -1;
            int Min = INF;
            for(int i = 0; i < n; i++)
                if(!vis[i] && lowcost[i] < Min) {
                    Min = lowcost[i];
                    k = i;
                }
            if(k == -1)break;
            vis[k] = true;
            for(int i = 0; i < n; i++)
                if(!vis[i] && lowcost[k] + cost[k][i] < lowcost[i]) {
                    lowcost[i] = lowcost[k] + cost[k][i];
                    pre[i] = k;
                }
        }
    }
    1.2 Dijkstar 算法 +堆优化
    /*
    * 使用优先队列优化Dijkstra算法
    * 复杂度O(ElogE)
    * 注意对vector<Edge>E[MAXN]进行初始化后加边
    */
    const int INF = 0x3f3f3f3f;
    const int MAXN = 1000010;
    struct qnode {
        int v;
        int c;
        qnode(int _v = 0, int _c = 0): v(_v), c(_c) {}
        bool operator <(const qnode &r)const {
            return c > r.c;
        }
    };
    struct Edge {
        int v, cost;
        Edge(int _v = 0, int _cost = 0): v(_v), cost(_cost) {}
    };
    vector<Edge>E[MAXN];
    bool vis[MAXN];
    int dist[MAXN];
    void Dijkstra(int n, int start) { //点的编号从1开始
        memset(vis, false, sizeof(vis));
        for(int i = 1; i <= n; i++)dist[i] = INF;
        priority_queue<qnode>que;
        while(!que.empty())que.pop();
        dist[start] = 0;
        que.push(qnode(start, 0));
        qnode tmp;
        while(!que.empty()) {
            tmp = que.top();
            que.pop();
            int u = tmp.v;
            if(vis[u])continue;
            vis[u] = true;
            for(int i = 0; i < E[u].size(); i++) {
                int v = E[tmp.v][i].v;
                int cost = E[u][i].cost;
                if(!vis[v] && dist[v] > dist[u] + cost) {
                    dist[v] = dist[u] + cost;
                    que.push(qnode(v, dist[v]));
                }
            }
        }
    }
    void addedge(int u, int v, int w) {
        E[u].push_back(Edge(v, w));
    }
    
    1.3 单源最短路 单源最短路 bellman_ford
    /*
    * 单源最短路bellman_ford算法,复杂度O(VE)
    * 可以处理负边权图。
    * 可以判断是否存在负环回路。返回true,当且仅当图中不包含从源点可达的负权回路
    * vector<Edge>E;先E.clear()初始化,然后加入所有边
    * 点的编号从1开始(从0开始简单修改就可以了)
    */
    const int INF = 0x3f3f3f3f;
    const int MAXN = 550;
    int dist[MAXN];
    struct Edge {
        int u, v;
        int cost;
        Edge(int _u = 0, int _v = 0, int _cost = 0): u(_u), v(_v), cost(_cost) {}
    };
    vector<Edge>E;
    bool bellman_ford(int start, int n) { //点的编号从1开始
        for(int i = 1; i <= n; i++)dist[i] = INF;
        dist[start] = 0;
        for(int i = 1; i < n; i++) { //最多做n-1次
            bool flag = false;
            for(int j = 0; j < E.size(); j++) {
                int u = E[j].u;
                int v = E[j].v;
                int cost = E[j].cost;
                if(dist[v] > dist[u] + cost) {
                    dist[v] = dist[u] + cost;
                    flag = true;
                }
            }
            if(!flag)return true;//没有负环回路
        }
        for(int j = 0; j < E.size(); j++)
            if(dist[E[j].v] > dist[E[j].u] + E[j].cost)
                return false;//有负环回路
        return true;//没有负环回路
    }
    
    1.4 单源最短路 单源最短路 单源最短路 SPFA
    /*
    * 单源最短路SPFA
    * 时间复杂度 0(kE)
    * 这个是队列实现,有时候改成栈实现会更加快,很容易修改
    * 这个复杂度是不定的
    */
    const int MAXN = 1010;
    const int INF = 0x3f3f3f3f;
    struct Edge {
        int v;
        int cost;
        Edge(int _v = 0, int _cost = 0): v(_v), cost(_cost) {}
    };
    vector<Edge>E[MAXN];
    void addedge(int u, int v, int w) {
        E[u].push_back(Edge(v, w));
    }
    bool vis[MAXN];//在队列标志
    int cnt[MAXN];//每个点的入队列次数
    int dist[MAXN];
    bool SPFA(int start, int n) {
        memset(vis, false, sizeof(vis));
        for(int i = 1; i <= n; i++)dist[i] = INF;
        vis[start] = true;
        dist[start] = 0;
        queue<int>que;
        while(!que.empty())que.pop();
        que.push(start);
        memset(cnt, 0, sizeof(cnt));
        cnt[start] = 1;
        while(!que.empty()) {
            int u = que.front();
            que.pop();
            vis[u] = false;
            for(int i = 0; i < E[u].size(); i++) {
                int v = E[u][i].v;
                if(dist[v] > dist[u] + E[u][i].cost) {
                    dist[v] = dist[u] + E[u][i].cost;
                    if(!vis[v]) {
                        vis[v] = true;
                        que.push(v);
                        if(++cnt[v] > n)return false;
    //cnt[i]为入队列次数,用来判定是否存在负环回路
                    }
                }
            }
        }
        return true;
    }
    

      

    8.欧拉回路Fleury

    /*删边要注意复杂度,尽量别用标记删除,而是直接删除
    无向图满足欧拉回路:度为偶数,或者度为奇数的点个数为2
    有向图满足欧拉回路:入度全部等于出度,或者1 个点入度-出度=1,一个点出度-入度=1,其他点入度等于出度
    */
    void Fleury(int u) {
        for(int i = Head[u]; ~i; i = Head[u]) {
            Head[u] = E[i].nxt;
            if(!vis[i | 1]) {
                int v = E[i].v;
                vis[i | 1] = 1;
                Fleury(v);
            }
        } 
        Path[++r] = u;
    }
    

      

    数论

    1.中国剩余定理

    LL gcd(LL a, LL b, LL &x, LL &y) {  
        if(b == 0) {  
            x =1, y = 0;  
            return a;  
        }  
        LL r = gcd(b, a % b, x, y);  
        LL t  = y;  
        y = x - a / b * y;  
        x = t;  
        return r;  
    }  
       
    LL multi(LL a, LL b, LL mod) {  
        LL ret = 0;  
        while(b) {  
            if(b & 1) {  
                ret = ret + a;  
                if(ret >= mod) ret -= mod;  
            }  
            a = a + a;  
            if(a >= mod) a -= mod;  
            b >>= 1;  
        }  
        return ret;  
    }  
       
    LL crt(int n, LL m[], LL a[]) {  
        LL M = 1, d, y, x = 0;  
        for(int i = 0; i < n; i++) M *= m[i];  
        for(int i = 0; i < n; i++) {  
            LL w = M / m[i];  
            d = gcd(m[i], w, d, y);  
            y = (y % m[i] + m[i]) % m[i];  
            x = ((x + multi(multi(a[i], w, M), y, M)) % M + M) % M;  
        }  
        return x;  
    }  
    

      

    2.大质数判定

    LL multi(LL a, LL b, LL mod) {
        LL ret = 0;
        while(b) {
            if(b & 1) ret = ret + a;
            if(ret >= mod) ret -= mod;
            a = a + a;
            if(a >= mod) a -= mod;
            b >>= 1;
        }
        return ret;
    }
    LL power(LL a, LL b, LL mod) {
        LL ret = 1;
        while(b) {
            if(b & 1) ret = multi(ret, a, mod);
            a = multi(a, a, mod);
            b >>= 1;
        }
        return ret;
    }
    bool Miller_Rabin(LL n) {
        LL u = n - 1, pre, x;
        int i, j, k = 0;
        if(n == 2 || n == 3 || n == 5 || n == 7 || n == 11) return true;
        if(n == 1 || (!(n % 2)) || (!(n % 3)) || (!(n % 5)) || (!(n % 7)) || (!(n % 11))) return
                false;
        for(; !(u & 1); k++, u >>= 1);
        for(i = 0; i < 5; i++) {
            x = rand() % (n - 2) + 2;
            x = power(x, u, n);
            pre = x;
            for(j = 0; j < k; j++) {
                x = multi(x, x, n);
                if(x == 1 && pre != 1 && pre != (n - 1))
                    return false;
                pre = x;
            }
            if(x != 1) return false;
        }
        return true;
    }
    

    3.约瑟夫环

    /*
    F[n] = (F[n - 1] + m) % n, F[1] = 0
    返回的下标从0 开始,复杂度大约为O(m)
    */
    int Joseph(int n, int m) {
        if(n == 1) return 0;
        if(m == 1) return n - 1;
        LL pre = 0; int now = 2;
        while(now <= n) {
            if(pre + m >= now) {
                pre = (pre + m) % now;
                now++;
            } else {
                int a = now - 1 - pre, b = m - 1;
                int k = a / b + (a % b != 0);
                if(now + k > n + 1) k = n + 1 - now;
                pre = (pre + (LL)m * k) % (now + k - 1);
                now += k;
            }
        }
        return pre;
    }
    

      

     

    博弈

    1.Bash博弈
    题意:一堆石头共n个,两人轮流取,最少取一个,最多取m个。最后取完的人赢
    思路:当m % (n + 1) != 0时先手赢,否则后手赢

    2.威佐夫博弈
    题意:两堆石子数量分别为a,b。两人轮流取石子,取石子时可以1.从一堆中取任意个
    数 2.在两堆中取相同个数。最后取完的人获胜。
    思路:满足黄金分割
    if(a >= b) swap(a, b);
    int k = b - a;
    int x = (sqrt(5.0) + 1) / 2 * k, y = x + k;
    if(a == x && b == y) printf("B ");
    else printf("A ");

    3.Nim博弈
    题意:有n堆石子。A B两个人轮流拿,A先拿。每次只能从一堆中取若干个,可将一堆
    全取走,但不可不取,拿到最后1颗石子的人获胜。
    思路:Nim博弈相当于把独立游戏分开计算SG函数,然后再用位异或
    SG[u] = Mex({后继的集合})相当于取出最小的集合中不存在的数字,可以发现mex的值
    总是比后继的个数要少,而且vis数组通常都是开在函数内部,不开在全局变量中,防
    止冲突

    4.斐波那契博弈
    题意:1堆石子n个,第一个人可以取任意个数但不能全部取完,以后每次拿的个数不能
    超过上一次对手拿的个数的2倍,轮流拿石子。
    思路:后手赢得情况的数字会呈现斐波那契数列

    5.Anti-num 博弈
    SG函数的求法一模一样最后如果只有一堆也能用SJ定理
    如果为Anti-Nim 游戏,如下情况先手胜
    SG异或和为0,且单个游戏的SG全部<=1
    SG 异或不为0,且存在单个游戏的 SG>1,即<=1的个数不等于独立游戏个数

    6.SG函数

    /**
    * 求出SG函数
    * F(存储可以走的步数,Array[0]表示可以有多少种走法)
    * F[]需要从小到大排序
    * 1.可选步数为1-m的连续整数,直接取模即可,SG(x)=x%(m+1);
    * 2.可选步数为任意步,SG(x) = x;
    * 3.可选步数为一系列不连续的数,用GetSG(计算)
    **/
    int SG[MAX], Hash[MAX];
    void init(int F[], int n) {
        int i, j;
        memset(SG, 0, sizeof(SG));
        for(i = 0; i <= n; i++) {
            memset(Hash, 0, sizeof(Hash));
            for(j = 1; j <= F[0]; j++) {
                if(i < F[j])
                    break;
                Hash[SG[i - F[j]]] = 1;
            }
            for(j = 0; j <= n; j++) {
                if(Hash[j] == 0) {
                    SG[i] = j;
                    break;
                }
            }
        }
    }
    
    /**
    * 获得一个单独的SG值
    * k为可走步数,F数组存储可走步数(0~k-1)
    */
    int F[101], sg[10001], k;
    int getsg(int m) {
        int hash[101] = {0};
        int i;
        for(i = 0; i < k; i++) {
            if(m - F[i] < 0)
                break;
            if(sg[m - F[i]] == -1)
                sg[m - F[i]] = getsg(m - F[i]);
            hash[sg[m - F[i]]] = 1;
        }
        for(i = 0;; i++)
            if(hash[i] == 0)
                return i;
    }
    

      

    其他

    1.祖传头文件

    #include <iostream>
    #include <cstring>
    #include <stdio.h>
    #include <algorithm>
    #include <queue>
    #include <vector>
    #include <iomanip>
    #include <math.h>
    #include <map>
    using namespace std;
    #define FIN     freopen("input.txt","r",stdin);
    #define FOUT    freopen("output.txt","w",stdout);
    #define INF     0x3f3f3f3f
    #define INFLL   0x3f3f3f3f3f3f3f
    #define lson    l,m,rt<<1
    #define rson    m+1,r,rt<<1|1
    typedef long long LL;
    typedef pair<int, int> PII;
    

      

    2.大数

    const int MX = 2500;
    const int MAXN = 9999;
    const int DLEN = 4;
    /*已重载>+-  % 和print*/
    class Big {
    public:
        int a[MX], len;
        Big(const int b = 0) {
            int c, d = b;
            len = 0;
            memset(a, 0, sizeof(a));
            while(d > MAXN) {
                c = d - (d / (MAXN + 1)) * (MAXN + 1);
                d = d / (MAXN + 1);
                a[len++] = c;
            }
            a[len++] = d;
        }
        Big(const char *s) {
            int t, k, index, L, i;
            memset(a, 0, sizeof(a));
            L = strlen(s);
            len = L / DLEN;
            if(L % DLEN) len++;
            index = 0;
            for(i = L - 1; i >= 0; i -= DLEN) {
                t = 0;
                k = i - DLEN + 1;
                if(k < 0) k = 0;
                for(int j = k; j <= i; j++) {
                    t = t * 10 + s[j] - '0';
                }
                a[index++] = t;
            }
        }
        Big operator/(const int &b)const {
            Big ret;
            int i, down = 0;
            for(int i = len - 1; i >= 0; i--) {
                ret.a[i] = (a[i] + down * (MAXN + 1)) / b;
                down = a[i] + down * (MAXN + 1) - ret.a[i] * b;
            }
            ret.len = len;
            while(ret.a[ret.len - 1] == 0 && ret.len > 1) ret.len--;
            return ret;
        }
        bool operator>(const Big &T)const {
            int ln;
            if(len > T.len) return true;
            else if(len == T.len) {
                ln = len - 1;
                while(a[ln] == T.a[ln] && ln >= 0) ln--;
                if(ln >= 0 && a[ln] > T.a[ln]) return true;
                else return false;
            } else return false;
        }
        Big operator+(const Big &T)const {
            Big t(*this);
            int i, big;
            big = T.len > len ? T.len : len;
            for(i = 0; i < big; i++) {
                t.a[i] += T.a[i];
                if(t.a[i] > MAXN) {
                    t.a[i + 1]++;
                    t.a[i] -= MAXN + 1;
                }
            }
            if(t.a[big] != 0) t.len = big + 1;
            else t.len = big;
            return t;
        }
        Big operator-(const Big &T)const {
            int i, j, big;
            bool flag;
            Big t1, t2;
            if(*this > T) {
                t1 = *this;
                t2 = T;
                flag = 0;
            } else {
                t1 = T;
                t2 = *this;
                flag = 1;
            }
            big = t1.len;
            for(i = 0; i < big; i++) {
                if(t1.a[i] < t2.a[i]) {
                    j = i + 1;
                    while(t1.a[j] == 0) j++;
                    t1.a[j--]--;
                    while(j > i) t1.a[j--] += MAXN;
                    t1.a[i] += MAXN + 1 - t2.a[i];
                } else t1.a[i] -= t2.a[i];
            }
            t1.len = big;
            while(t1.a[t1.len - 1] == 0 && t1.len > 1) {
                t1.len--;
                big--;
            }
            if(flag) t1.a[big - 1] = 0 - t1.a[big - 1];
            return t1;
        }
        int operator%(const int &b)const {
            int i, d = 0;
            for(int i = len - 1; i >= 0; i--) {
                d = ((d * (MAXN + 1)) % b + a[i]) % b;
            }
            return d;
        }
        Big operator*(const Big &T) const {
            Big ret;
            int i, j, up, temp, temp1;
            for(i = 0; i < len; i++) {
                up = 0;
                for(j = 0; j < T.len; j++) {
                    temp = a[i] * T.a[j] + ret.a[i + j] + up;
                    if(temp > MAXN) {
                        temp1 = temp - temp / (MAXN + 1) * (MAXN + 1);
                        up = temp / (MAXN + 1);
                        ret.a[i + j] = temp1;
                    } else {
                        up = 0;
                        ret.a[i + j] = temp;
                    }
                }
                if(up != 0) {
                    ret.a[i + j] = up;
                }
            }
            ret.len = i + j;
            while(ret.a[ret.len - 1] == 0 && ret.len > 1) ret.len--;
            return ret;
        }
        void print() {
            printf("%d", a[len - 1]);
            for(int i = len - 2; i >= 0; i--) printf("%04d", a[i]);
        }
    };
    

      

    3.STL

    1.vector
    构造
    vector <int> v;
    基本操作
    v.begin()             //指向迭代器中第一个元素。  
    v.end()               //指向迭代器中末端元素的下一个,指向一个不存在元素。        
    v.push_back(elem)     //在尾部加入一个数据。 
    v.pop_back()          //删除最后一个数据。 
    v.capacity()          //vector可用空间的大小。 
    v.size()              //返回容器中数据个数。 
    v.empty()             //判断容器是否为空。 
    v.front()             //传回第一个数据。 
    v.back()              //传回最后一个数据,不检查这个数据是否存在。 
    v.at(index)           //传回索引idx所指的数据,如果idx越界,抛出out_of_range。 
    v.clear()             //移除容器中所有数据。 
    v.erase(iterator)     //删除pos位置的数据,传回下一个数据的位置。 
    v.erase(begin,end)    //删除[beg,end)区间的数据,传回下一个数据的位置。注意:begin和end为iterator 
    v.insert(position,elem)      //在pos位置插入一个elem拷贝,传回新数据位置。 
    v.insert(position,n,elem)    //在pos位置插入n个elem数据,无返回值。 
    v.insert(position,begin,end) //在pos位置插入在[beg,end)区间的数据,无返回值。
      
    
    2.priority_queue
    构造
    priority_queue<int>que;                             //采用默认优先级构造队列最大值先出队
    priority_queue<int,vector<int>,greater<int> >que3;  //最小值先出队
    priority_queue<int,vector<int>,less<int> >que4;     //最大值先出队
    基本操作
    que.empty()   //判断一个队列是否为空
    que.pop()     //删除队顶元素
    que.push()    //加入一个元素
    que.size()    //返回优先队列中拥有的元素个数
    que.top()     //返回优先队列的队顶元素
      
    3.map
    构造
    map<int, string> mp;
    基本操作
    mp.begin()          //返回指向map头部的迭代器
    mp.clear()         //删除所有元素
    mp.count()          //返回指定元素出现的次数
    mp.empty()          //如果map为空则返回true
    mp.end()            //返回指向map末尾的迭代器
    mp.equal_range()    //返回特殊条目的迭代器对
    mp.erase()          //删除一个元素
    mp.find()           //查找一个元素
    mp.get_allocator()  //返回map的配置器
    mp.insert()         //插入元素
    mp.key_comp()       //返回比较元素key的函数
    mp.lower_bound()    //返回键值>=给定元素的第一个位置
    mp.max_size()       //返回可以容纳的最大元素个数
    mp.rbegin()         //返回一个指向map尾部的逆向迭代器
    mp.rend()           //返回一个指向map头部的逆向迭代器
    mp.size()           //返回map中元素的个数
    mp.swap()           //交换两个map
    mp.upper_bound()    //返回键值>给定元素的第一个位置
    mp.value_comp()     //返回比较元素value的函数
     
    4.queue
    构造
    queue<int> q;
    基本操作 
    q.push(num)   //入列,插入队列的末端。 
    q.empty()     //判断队列是否为空
    q.size()      //获取队列中的元素个数   
    q.back()      //访问队列尾元素    
      
    5.stack
    构造
    stack<int> s;
    stack<int> c1(c2);  复制stack
    基本操作
    s.top()        //返回栈顶数据
    s.push(num)   //在栈顶插入数据
    s.pop()       //弹出栈顶数据
    s.empty()     //判断栈是否为空
    s.size()      //返回栈中数据的个数
      
    6.set
    构造
    set<int> s;
    基本操作
    s.insert(num)             //把元素插入到集合中,同一元素不会重复插入
    s.inset(first,second)     //将定位器first到second之间的元素插入到set中,返回值是void
    s.erase(6)                //删除键值为6的元素 
    s.find(6)                 //查找键值为6的元素,如果没找到则返回end()
    s.begin()                //返回set容器的第一个元素
    s.end()                //返回set容器的最后一个元素
    s.clear()               //删除set容器中的所有的元素
    s.empty()              //判断set容器是否为空
    s.max_size()            //返回set容器可能包含的元素最大个数
    s.size()              //返回当前set容器中的元素个数
    s.rbegin()            //返回的值和end()相同
    s.rend()              //返回的值和rbegin()相同
    s.count()                //用来查找set中某个某个键值出现的次数,一般用于判断某一键值是否在set出现过
    erase(iterator)          //删除定位器iterator指向的值
    erase(first,second)      //删除定位器first和second之间的值(不包括second)
    erase(key_value)         //删除键值key_value的值
    lower_bound(key_value)   //返回第一个大于等于key_value的定位器
    upper_bound(key_value)   //返回最后一个大于等于key_value的定位器
      
    7.deque
    构造
    deque<int> c;
    deque<int> c1(c2);      //复制deque
    deque<int> c(n, elem);  //创建一个含有n个elem拷贝的deque
    基本操作
    c.assign(beg, end);  //将beg-end区间中的值赋值给c
    c.assign(n, elem);   //将n个elem的拷贝赋值给c
    c.at(idx);       //返回idx位置所指数据
    c.front();      //返回第一个数据
    c.back();      //返回最后一个数据
    c.begin();     //返回指向第一个数据的迭代器
    c.end();       //返回指向最后一个数据的下一个位置的迭代器
    c.rbegin();    //返回逆向队列的第一个数据
    c.rend();      //返回指向逆向队列的最后一个数据的下一个位置的迭代器
    c.push_back(elem);  //在尾部加入一个数据
    c.push_front(elem);  //在头部插入一个数据
    c.insert(pos, elem);  //在pos位置插入一个elem拷贝,返回新数据位置
    c.insert(pos, n, elem);  //在pos位置插入n个elem数据,无返回值
    c.insert(pos, beg, end);  //在pos位置插入在beg-end区间的数据,无返回值
    c.pop_back();           //删除最后一个数据
    c.pop_front();         //删除头部数据
    c.erase(pos);         //删除pos位置的数据,返回下一个数据的位置
    c.erase(beg, end);    //删除beg-end区间的数据,返回下一个数据的位置
    c.empty();            //判断容器是否为空
    c.max_size();         //返回容器中最大数据的数量
    c.resize(num);       //重新指定队列的长度
    c.size();            //返回容器中实际数据的个数
    c1.swap(c2)          //将c1和c2元素互换
      
    

      

    4.正常的读入挂

    inline int read() {
        int ret = 0, c, f = 1;
        for(c = getchar(); !(isdigit(c) || c == '-'); c = getchar());
        if(c == '-') f = -1, c = getchar();
        for(; isdigit(c); c = getchar()) ret = ret * 10 + c - '0';
        if(f < 0) ret = -ret;
        return ret;
    }
    

      

    5.fread读入挂

    namespace IO {
    const int MX = 1e7; //1e7 占用内存11000kb
    char buf[MX]; int c, sz;
    void begin() {
        c = 0;
        sz = fread(buf, 1, MX, stdin);
    } 
    inline bool read(int &t) {
        while(c < sz && buf[c] != '-' && (buf[c] < '0' || buf[c] > '9')) c++;
        if(c >= sz) return false;
        bool flag = 0;
        if(buf[c] == '-') flag = 1, c++;
        for(t = 0; c < sz && '0' <= buf[c] && buf[c] <= '9'; c++) t = t * 10 + buf[c] - '0';
        if(flag) t = -t;
        return true;
    }
    }
    

      

    6.莫队算法

    莫队算法,可以解决一类静态,离线区间查询问题。
    BZOJ 2038: [2009 国家集训队]小Z 的袜子(hose)
    Description
    作为一个生活散漫的人,小Z 每天早上都要耗费很久从一堆五颜六色的袜子中找出一双来穿。
    终于有一天,小Z 再也无法忍受这恼人的找袜子过程,于是他决定听天由命…… 具体来说,
    小Z 把这N 只袜子从1 到N 编号,然后从编号L 到R(L
    Input
    输入文件第一行包含两个正整数N 和M。N 为袜子的数量,M 为小Z 所提的询问的数量。
    接下来一行包含N 个正整数Ci,其中Ci 表示第i 只袜子的颜色,相同的颜色用相同的数字
    表示。再接下来M 行,每行两个正整数L,R 表示一个询问。
    Output
    包含M 行,对于每个询问在一行中输出分数A / B 表示从该询问的区间[L, R]中随机抽出两只
    袜子颜色相同的概率。若该概率为0 则输出0 / 1,否则输出的A / B 必须为最简分数。(详见
    样例)
    Sample Input
    6 4
    1 2 3 3 3 2
    2 6
    1 3
    3 5
    1 6
    Sample Output
    2 / 5
    0 / 1
    1 / 1
    4 / 15 
    
    只需要统计区间内各个数出现次数的平方和
    莫队算法,两种方法,一种是直接分成sqrt(n)块,分块排序。
    另外一种是求得曼哈顿距离最小生成树,根据manhattan MST 的dfs 序求解。
    1 分块
    const int MAXN = 50010;
    const int MAXM = 50010;
    struct Query {
        int L, R, id;
    } node[MAXM];
    long long gcd(long long a, long long b) {
        if(b == 0)return a;
        return gcd(b, a % b);
    }
    struct Ans {
        long long a, b; //分数a/b
        void reduce() { //分数化简
            long long d = gcd(a, b);
            a /= d; b /= d;
        }
    } ans[MAXM];
    int a[MAXN];
    int num[MAXN];
    int n, m, unit;
    bool cmp(Query a, Query b) {
        if(a.L / unit != b.L / unit)return a.L / unit < b.L / unit;
        else return a.R < b.R;
    }
    void work() {
        long long temp = 0;
        memset(num, 0, sizeof(num));
        int L = 1;
        int R = 0;
        for(int i = 0; i < m; i++) {
            while(R < node[i].R) {
                R++;
                temp -= (long long)num[a[R]] * num[a[R]];
                num[a[R]]++;
                temp += (long long)num[a[R]] * num[a[R]];
            }
            while(R > node[i].R) {
                temp -= (long long)num[a[R]] * num[a[R]];
                num[a[R]]--;
                temp += (long long)num[a[R]] * num[a[R]];
                R--;
            }
            while(L < node[i].L) {
                temp -= (long long)num[a[L]] * num[a[L]];
                num[a[L]]--;
                temp += (long long)num[a[L]] * num[a[L]];
                L++;
            }
            while(L > node[i].L) {
                L--;
                temp -= (long long)num[a[L]] * num[a[L]];
                num[a[L]]++;
                temp += (long long)num[a[L]] * num[a[L]];
            }
            ans[node[i].id].a = temp - (R - L + 1);
            ans[node[i].id].b = (long long)(R - L + 1) * (R - L);
            ans[node[i].id].reduce();
        }
    }
    
    int main() {
        //FIN
        while(~scanf("%d%d", &n, &m)) {
            for(int i = 1; i <= n; i++) scanf("%d", &a[i]);
            for(int i = 0; i < m; i++) {
                node[i].id = i;
                scanf("%d%d", &node[i].L, &node[i].R);
            }
            unit = sqrt(n);
            sort(node , node + m, cmp);
            work();
            for(int i = 0; i < m; i++) printf("%lld/%lld
    ", ans[i].a, ans[i].b);
    
        }
        return 0;
    }
    
    2 Manhattan MST的dfs顺序求解
    const int MAXN = 50010;
    const int MAXM = 50010;
    const int INF = 0x3f3f3f3f;
    struct Point {
        int x, y, id;
    } p[MAXN], pp[MAXN];
    bool cmp(Point a, Point b) {
        if(a.x != b.x) return a.x < b.x;
        else return a.y < b.y;
    }
    //树状数组,找y-x大于当前的,但是y+x最小的
    struct BIT {
        int min_val, pos;
        void init() {
            min_val = INF;
            pos = -1;
        }
    } bit[MAXN];
    struct Edge {
        int u, v, d;
    } edge[MAXN << 2];
    bool cmpedge(Edge a, Edge b) {
        return a.d < b.d;
    }
    int tot;
    int n;
    int F[MAXN];
    int find(int x) {
        if(F[x] == -1) return x;
        else return F[x] = find(F[x]);
    }
    void addedge(int u, int v, int d) {
        edge[tot].u = u;
        edge[tot].v = v;
        edge[tot++].d = d;
    }
    struct Graph {
        int to, next;
    } e[MAXN << 1];
    int total, head[MAXN];
    void _addedge(int u, int v) {
        e[total].to = v;
        e[total].next = head[u];
        head[u] = total++;
    }
    int lowbit(int x) {
        return x & (-x);
    }
    void update(int i, int val, int pos) {
        while(i > 0) {
            if(val < bit[i].min_val) {
                bit[i].min_val = val;
                bit[i].pos = pos;
            }
            i -= lowbit(i);
        }
    }
    int ask(int i, int m) {
        int min_val = INF, pos = -1;
        while(i <= m) {
            if(bit[i].min_val < min_val) {
                min_val = bit[i].min_val;
                pos = bit[i].pos;
            }
            i += lowbit(i);
        }
        return pos;
    }
    int dist(Point a, Point b) {
        return abs(a.x - b.x) + abs(a.y - b.y);
    }
    void Manhattan_minimum_spanning_tree(int n, Point p[]) {
        int a[MAXN], b[MAXN];
        tot = 0;
        for(int dir = 0; dir < 4; dir++) {
            if(dir == 1 || dir == 3) {
                for(int i = 0; i < n; i++)
                    swap(p[i].x, p[i].y);
            } else if(dir == 2) {
                for(int i = 0; i < n; i++)
                    p[i].x = -p[i].x;
            }
            sort(p, p + n, cmp);
            for(int i = 0; i < n; i++)
                a[i] = b[i] = p[i].y - p[i].x;
            sort(b, b + n);
            int m = unique(b, b + n) - b;
            for(int i = 1; i <= m; i++)
                bit[i].init();
            for(int i = n - 1; i >= 0; i--) {
                int pos = lower_bound(b, b + m, a[i]) - b + 1;
                int ans = ask(pos, m);
                if(ans != -1)
                    addedge(p[i].id, p[ans].id, dist(p[i], p[ans]));
                update(pos, p[i].x + p[i].y, i);
            }
        }
        memset(F, -1, sizeof(F));
        sort(edge, edge + tot, cmpedge);
        total = 0;
        memset(head, -1, sizeof(head));
        for(int i = 0; i < tot; i++) {
            int u = edge[i].u, v = edge[i].v;
            int t1 = find(u), t2 = find(v);
            if(t1 != t2) {
                F[t1] = t2;
                _addedge(u, v);
                _addedge(v, u);
            }
        }
    }
    int m;
    int a[MAXN];
    struct Ans {
        long long a, b;
    } ans[MAXM];
    long long temp ;
    int num[MAXN];
    void add(int l, int r) {
        for(int i = l; i <= r; i++) {
            temp -= (long long)num[a[i]] * num[a[i]];
            num[a[i]]++;
            temp += (long long)num[a[i]] * num[a[i]];
        }
    }
    void del(int l, int r) {
        for(int i = l; i <= r; i++) {
            temp -= (long long)num[a[i]] * num[a[i]];
            num[a[i]]--;
            temp += (long long)num[a[i]] * num[a[i]];
        }
    }
    void dfs(int l1, int r1, int l2, int r2, int idx, int pre) {
        if(l2 < l1) add(l2, l1 - 1);
        if(r2 > r1) add(r1 + 1, r2);
        if(l2 > l1) del(l1, l2 - 1);
        if(r2 < r1) del(r2 + 1, r1);
        ans[pp[idx].id].a = temp - (r2 - l2 + 1);
        ans[pp[idx].id].b = (long long)(r2 - l2 + 1) * (r2 - l2);
        for(int i = head[idx]; i != -1; i = e[i].next) {
            int v = e[i].to;
            if(v == pre) continue;
            dfs(l2, r2, pp[v].x, pp[v].y, v, idx);
        }
        if(l2 < l1)del(l2, l1 - 1);
        if(r2 > r1)del(r1 + 1, r2);
        if(l2 > l1)add(l1, l2 - 1);
        if(r2 < r1)add(r2 + 1, r1);
    }
    long long gcd(long long a, long long b) {
        if(b == 0) return a;
        else return gcd(b, a % b);
    }
    int main() {
        while(scanf("%d%d", &n, &m) == 2) {
            for(int i = 1; i <= n; i++)
                scanf("%d", &a[i]);
            for(int i = 0; i < m; i++) {
                scanf("%d%d", &p[i].x, &p[i].y);
                p[i].id = i;
                pp[i] = p[i];
            }
            Manhattan_minimum_spanning_tree(m, p);
            memset(num, 0, sizeof(num));
            temp = 0;
            dfs(1, 0, pp[0].x, pp[0].y, 0, -1);
            for(int i = 0; i < m; i++) {
                long long d = gcd(ans[i].a, ans[i].b);
                printf("%lld/%lld
    ", ans[i].a / d, ans[i].b / d);
            }
        }
        return 0;
    }
    

      

     

  • 相关阅读:
    使用FileReader在浏览器读取预览文件(image和txt)
    移动端Vue图片获取,压缩,预览组件-upload-img(H5+获取照片,压缩,预览)
    文件(图片)转base64
    Vue单页面应用打包app处理返回按钮
    hbuilder/hbuilderx 无法检测到模拟器
    不启动AndroidStudio直接启动其模拟器
    ES6,箭头函数 (=>)注意点
    ES6,扩展运算符
    strcmp使用注意
    android11 gc5035前置摄像头当作后置使用左右镜像问题
  • 原文地址:https://www.cnblogs.com/Hyouka/p/7200621.html
Copyright © 2020-2023  润新知