模板整理 (施工中 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("]\n");
        }
    }
};

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\n");
else printf("A\n");

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\n", 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\n", ans[i].a / d, ans[i].b / d);
        }
    }
    return 0;
}

  

 

posted @ 2017-08-30 12:52  Hyouka  阅读(279)  评论(0编辑  收藏  举报