省选模板

省选模板
基础篇
1.高精度
const int maxD = 1010;
struct Huge
{
    int len, ele[maxD];
    Huge(): len(1) {memset(ele, 0, sizeof ele);}
    Huge &operator=(const Huge &b)
    {
        memcpy(ele, b.ele, sizeof ele);
        len = b.len; return *this;
    }
    int &operator[](int Ind) {return ele[Ind];}
};
int cmp(const Huge &a, const Huge &b)
{
    int tmp = (a.len>b.len ? a.len : b.len)-1;
    while(tmp>-1&& a[tmp]==b[tmp])--tmp;
    return tmp>-1 ? a[tmp]-b[tmp] : 0;
}
void plus(const Huge &a, const Huge &b, Huge &c)
{
    int len=a.len>b.len? a.len: b.len; c=Huge();
    for (int i = 0; i < len; ++i)
    {
        c[i] += a[i] + b[i];
        if (c[i] >= 10) c[i] -= 10, ++c[i + 1];
    }
    if (c[len]) ++len; c.len = len;
}
void subtract(const Huge&a,const Huge&b,Huge&c)
{
    int len=a.len>b.len? a.len: b.len; c=Huge();
    for (int i = 0; i < len; ++i)
    {
        c[i] += a[i] - b[i];
        if (c[i] < 0) c[i] += 10, --c[i + 1];
    }
    while (len>1 && !c[len-1]) --len;
    c.len = len;
}
void multiply10(Huge &a)
{
    for (int i = a.len; i; --i) a[i] = a[i - 1];
    a[0] = 0, ++a.len;
    while (a.len>1 && !a[a.len-1]) --a.len;
}
void multiply(const Huge &a, int b, Huge &c)
{
    int len = a.len; c = Huge();
    for (int i = 0; i < len; ++i)
    {
        c[i] += a[i] * b;
        c[i + 1] += c[i] / 10;
        c[i] %= 10;
    }
    for (++len; c[len - 1] >= 10; ++len)
        c[len] += c[len-1] / 10, c[len-1] %= 10;
    while (len > 1 && !c[len - 1]) --len;
    c.len = len;
}
void multiply(const Huge &a, const Huge &b, Huge &c)
{
    c = Huge();
    for (int i = 0; i < a.len; ++i)
    for (int j = 0; j < b.len; ++j)
    {
        c[i + j] += a[i] * b[j];
        c[i + j + 1] += c[i + j] / 10;
        c[i + j] %= 10;
    }
    int len = a.len + b.len + 1;
    while (len > 1 && !c[len - 1]) --len;
    c.len = len;
}
void divide(const Huge&a, int b, Huge&c, int&d)
{
    int len = a.len; c = Huge(); d = 0;
    for (int i = len - 1; i > -1; --i)
        d = d * 10 + a[i],
        c[i] = d / b, d %= b;
    while (len > 1 && !c[len - 1]) --len;
    c.len = len;
}
void divide(const Huge &a, const Huge &b,
              Huge &c, Huge &d)
{
    Huge e; c = d = Huge(); int len=a.len;
    for (int i = len - 1; i > -1; --i)
    {
        multiply10(d); d[0] = a[i];
        while(cmp(d, b) > -1)
        {subtract(d, b, e); d = e; ++c[i];}
    }
    while (len > 1 && !c[len - 1]) --len;
    c.len = len;
}

2.快速排序
template <typename _Tp>
void qsort(_Tp *data, int l, int r,
             bool (*cmp)(_Tp &, _Tp &))
{
    int i = l, j = r, x = data[(l + r) >> 1];
    do
    {
        while (cmp(data[i], x)) ++i;
        while (cmp(x, data[j])) --j;
        if (i<=j) {swap(data[i++], data[j--]);}
    } while (i < j);
    if (l < j) qsort(data, l, j, cmp);
    if (i < r) qsort(data, i ,r, cmp);
}

3.归并排序
void MergeSort(int L, int R)
{
    int Mid = L + R + 1 >> 1;
    if (L < Mid - 1) MergeSort(L, Mid);
    if (Mid < R - 1) MergeSort(Mid, R);
    for (int f1 = L, f2 = Mid, f = 0;
         f1 < Mid || f2 < R;)
    if (!(f2 < R) || f1 < Mid && a[f1] < a[f2])
        res[f++] = a[f1++];
    else res[f++] = a[f2++];
    for (int i=L; i<R; ++i) a[i] = res[i - L];
}

4.堆操作
inline void adjust_down(int i)
{
    while ((i <<= 1) <= top)
    {
        if (i < top && cmp(hp[i], hp[i+1])) ++i;
        if(cmp(hp[i >> 1], hp[i]))
            swap(hp[i >> 1], hp[i]);
        else break;
    }
}
inline void adjust_up(int i)
{
    for (; i >> 1; i >>= 1)
    if(cmp(hp[i>>1],hp[i]))swap(hp[i>>1],hp[i]);
    else break;
}

5.素数表生成器
inline void mkprime()
{
    top = 0;
    for (int i = 2; i <= n; ++i)
    {
        if (!tag[i])  p[top++] = i;
        for (int j=0; j<top && p[j]*i <= n; ++j)
        {
            tag[i * p[j]] = 1;
            if (i % p[j] == 0) break;
        }
    }
}

6.快速幂+矩阵乘法(迷路)
typedef int Matrix[maxN][maxN];
int tmp[maxR][maxR], n, T; Matrix mp = {0};
void readdata()
{
    scanf("%d%d", &n, &T);
    for (int i = 0; i < n; ++i)
    {
        getchar();
        for (int j = 0; j < n; ++j)
            tmp[i][j] = getchar() - '0';
    }
    for (int i = 0; i < n; ++i)
    for (int k = 1; k < maxT; ++k)
        mp[i * maxT + k - 1][i * maxT + k] = 1;
    for (int i = 0; i < n; ++i)
    for (int j = 0; j < n; ++j)
    if (tmp[i][j])
     //有边才能连边,否则无意义且出错。
        mp[i*maxT + tmp[i][j] - 1][j*maxT]=1;
    n *= maxT;
}
void Mul(Matrix &a, Matrix b)
{
    Matrix ans = {0};
    for (int i = 0; i < n; ++i)
    for (int j = 0; j < n; ++j)
    for (int k = 0; k < n; ++k)
        (ans[i][j] += a[i][k] * b[k][j]) %= MOD;
	//这里应注意容易出错。
    for (int i = 0; i < n; ++i)
    for (int j = 0; j < n; ++j)
        a[i][j] = ans[i][j];
}
void Power(Matrix &mp, int T)
{
    Matrix ans, tmp;
    for (int i = 0; i < n; ++i)
    for (int j = 0; j < n; ++j)
    {ans[i][j] = 0; tmp[i][j] = mp[i][j] % MOD;}
    for (int i = 0; i < n; ++i) ans[i][i] = 1;
    for (; T; T >>= 1)
    {if (T & 1) Mul(ans, tmp); Mul(tmp, tmp);}
    for (int i = 0; i < n; ++i)
    for (int j = 0; j < n; ++j)
        mp[i][j] = ans[i][j] % MOD;
}
void work()
{Power(mp, T); printf("%d\n", mp[0][n-maxT]);}

7.并查集
int f[maxN], n, m, p;
int Find(int x)
{return (f[x]==x)?x:f[x]=Find(f[x]);}
void Merge(int x, int y) {f[Find(y)] = Find(x);}
void Judge(int x, int y)
{cout<< (Find(x)==Find(y) ? “Yes\n” : “No\n”);}
void work()
{
    cin >> n >> m;
    for (int i = 1; i < n + 1; ++i) f[i] = i;
    for (int i = 0; i < m; ++i)
    {int x, y; cin >> x >> y; Merge(x, y);}
    cin >> p;
    for (int i = 0; i < p; ++i)
    {int x, y; cin >> x >> y; Judge(x, y);}
}

8.最大子立方(吃西瓜)
int s[60][60][60], x[60][60][60], s1[60][60];
int n, m, h, ans = -0x7fffff00;
int main()
{
    scanf("%ld%ld%ld", &n, &m, &h);
    for (int i = 1; i < n + 1; ++i)
    for (int j = 1; j < m + 1; ++j)
    for (int k = 1; k < h + 1; ++k)
        scanf("%ld", &x[i][j][k]);
    for (int i = 1; i < n + 1; ++i)
    for (int j = 1; j < m + 1; ++j)
    for (int k = 1; k < h + 1; ++k)
        s[i][j][k] = s[i - 1][j][k]+x[i][j][k];
    for (int i1 = 0; i1 < n; ++i1)
    for (int i2 = i1 + 1; i2 < n + 1; ++i2)
    {
        memset(s1, 0, sizeof(s1));
        for (int j = 1; j < m + 1; ++j)
        for (int k = 1; k < h + 1; ++k)
            s1[j][k] = s1[j - 1][k] +
            s[i2][j][k] - s[i1][j][k];
        for (int j1 = 0; j1 < m; ++j1)
        for (int j2 = j1 + 1; j2 < m + 1; ++j2)
        {
            int tmp = s1[j2][1] - s1[j1][1];
            for (int k = 2; k < h + 1; ++k)
            {
                tmp = std::max(tmp, 0);
                tmp += s1[j2][k] - s1[j1][k];
                ans = std::max(tmp, ans);
            }
        }
    }
    printf("%ld", ans); return 0;
}

9.悬线法求极大子矩形(糖果盒)
int a[maxN][maxN], H[maxN], sum[maxN][maxN];
int maxL[2][maxN], maxR[2][maxN];
int L[maxN], R[maxN], n, m, ans;
int main()
{
    n = getint(); m = getint();
    for (int i = 1; i < n + 1; ++i)
    for (int j = 1; j < m + 1; ++j)
        sum[i][j] = sum[i][j - 1]
            + (a[i][j] = getint());
    for (int i = 1; i < n + 1; ++i)
    for (int j = 1; j < m + 1; ++j)
        sum[i][j] += sum[i - 1][j];
    for (int j=1; j<=m; ++j) maxR[0][j] = m;
    R[m + 1] = m;
    //这里要赋初值,否则出错。
    for (int i = 1; i < n + 1; ++i)
    {
        int pst = (i & 1) ^ 1, ths = i & 1;
        for (int j = 1; j < m + 1; ++j)
        if (!a[i][j])
            H[j]=i, L[j]=j, maxL[ths][j]=0;
	//如果该行该列的点为障碍,
	//那么下一行的maxL值跟这一行无关,设为0。
	    else
        {
            L[j] = L[j - 1];
            maxL[ths][j]=max(L[j],maxL[pst][j]);
        }
        for (int j = m; j; --j)
        if (!a[i][j])
            R[j] = j - 1, maxR[ths][j] = m;
	//如果该行该列的点为障碍,
	//那么下一行的maxR值跟这一行无关,设为m。
        else
        {
            R[j] = R[j + 1];
            maxR[ths][j]=min(R[j],maxR[pst][j]);
            int _L=maxL[ths][j],_R=maxR[ths][j],
            _U = H[j];
            ans=max(ans,sum[i][_R]+sum[_U][_L]
                    -sum[i][_L]-sum[_U][_R]);
        }
    }
    printf("%d\n", ans); return 0;
}

10.多重背包(逃亡的准备)
int f[maxV], a[maxN], w[maxN], v[maxN], n, m;
void readdata()
{
    scanf("%d%d", &n, &m);
    for (int i = 1; i < n + 1; ++i)
        scanf("%d%d%d", a + i, w + i, v + i);
}
void work()
{
    for (int i = 1; i < n + 1; ++i)
    {
        if (a[i] * w[i] >= m)
        for (int j = w[i]; j < m + 1; ++j)
            f[j] = max(f[j], f[j-w[i]] + v[i]);
        else
        {
            for (int k = 1; k < a[i]; k <<= 1)
            {
                for (int j = m; j >= k*w[i]; --j)
                    f[j] = max(f[j], f[j - k*w[i]]
                                 + k * v[i]);
                a[i] -= k;
            }
            for (int j = m; j >= a[i]*w[i]; --j)
                f[j] = max(f[j], f[j-a[i]*w[i]]
                             + a[i] * v[i]);
        }
    }
    printf("%d", f[m]);
}

11.读入整数的优化:
无符号:
inline int getint()
{
    int res = 0; char tmp;
    while (!(isdigit(tmp = getchar())));
    do res = (res<<3) + (res<<1) + tmp - '0';
    while ((isdigit(tmp = getchar())));
    return res;
}
有符号:
inline int getint()
{
    int res = 0; char tmp; bool sgn = 1;
    do tmp = getchar();
    while (!isdigit(tmp) && tmp != '-');
    if (tmp=='-') {sgn = 0; tmp = getchar();}
    do res = (res<<3) + (res<<1) + tmp - '0';
    while (isdigit(tmp = getchar()));
    return sgn ? res : -res;
}

省选篇
1.线段树:(Mayor's Poster)
struct SegTree {int L, R, lc, rc, color;};
struct Seg {int pos, Ind, sym;} tree[maxN << 1];
Seg seg[maxN << 1]; bitset <maxN> marked;
int L[maxN], R[maxN], n, N, tot, cnt;
int cmp(const void *a, const void *b)
{return ((Seg *)a)->pos - ((Seg *)b)->pos;}
void Build(int L, int R)
{
    int Now = ++tot;
    tree[Now].L = L; tree[Now].R = R;
    tree[Now].lc=tree[Now].rc=tree[Now].color=0;
    //所有标记清零。
    if (L + 1 < R)
    {
        int Mid = (L + R) >> 1;
        tree[Now].lc = tot + 1; Build(L, Mid);
        tree[Now].rc = tot + 1; Build(Mid, R);
    } //递归建树。
}
void insert(int Now, int i)
{
    if (L[i]>tree[Now].R || R[i]<tree[Now].L)
        return;
    //若当前被插入的线段与这个区间无交集,则不需要遍历。
    if (L[i]<=tree[Now].L && R[i]>=tree[Now].R)
    {tree[Now].color = i; return;}
    //若该区间被完全复盖,直接染色并退出。
    if (tree[Now].color > -1)
    {
        tree[tree[Now].lc].color =
            tree[tree[Now].rc].color =
                tree[Now].color;
        tree[Now].color = -1;
    } //若开始时该区间为单色或无色,
    //则标记向下传,并把该区间标记为多色。
    int Mid = (tree[Now].L + tree[Now].R) >> 1;
    if (L[i] < Mid) insert(tree[Now].lc, i);
    if (Mid < R[i]) insert(tree[Now].rc, i);
    //依次对它的左右子区间进行统计。
}
void count(int Now)
{
    if (tree[Now].color == 0) return;
    //如果该区间没有颜色,那么其子区间不需要被遍历。
    if (tree[Now].color > 0)
    {
        if (!marked.test(tree[Now].color))
        {marked.set(tree[Now].color); ++cnt;}
        return;
    } //若该区间为单色,统计该颜色后
    //就不需要对其子区间进行统计了。
    if (tree[Now].lc) count(tree[Now].lc);
    if (tree[Now].rc) count(tree[Now].rc);
    //若该区间为多色,那么需要对各个子区间分别进行统计。
}
void work()
{
    scanf("%d", &N);
    for (; N; --N)
    {
        scanf("%d", &n); int x, y; tot = 0;
        for (int i = 1; i < n + 1; ++i)
        {
            scanf("%d%d", &x, &y);
            seg[++tot].pos = x;
            seg[tot].sym = 1; seg[tot].Ind = i;
            seg[++tot].pos = y + 1;
            seg[tot].sym = -1; seg[tot].Ind = i;
        }
        qsort(seg + 1, n<<1, sizeof seg[0], cmp);
        //按照座标从左到右排序,方便压缩。
        int Last = 1;
        if (seg[1].sym == 1) L[seg[1].Ind] = 1;
        if (seg[1].sym == -1) R[seg[1].Ind] = 1;
        for (int i = 2; i < (n << 1) + 1; ++i)
        if (seg[i].pos == seg[i - 1].pos)
        {
            if(seg[i].sym==1) L[seg[i].Ind]=Last;
            if(seg[i].sym==-1)R[seg[i].Ind]=Last;
        }
        else if (seg[i].pos > seg[i - 1].pos)
     //压缩区间,“毁掉”不需要用掉的空间。
        {
            if (seg[i].sym == 1)
                L[seg[i].Ind] = ++Last;
            if (seg[i].sym == -1)
                R[seg[i].Ind] = ++Last;
        }
        tot = 0; Build(1, Last);  //建立线段树。
        for (int i=1; i <= n; ++i) insert(1, i);
        marked.reset(); cnt = 0; count(1);
        printf("%d\n", cnt);
    }
}

2.平衡二叉树(SBT)
class SBT
{
private:
    int key[maxN << 2], sz[maxN << 2], T;
    int lc[maxN << 2], rc[maxN << 2], tot;
    void Zig(int &T)
    {
        int tmp = lc[T]; lc[T] = rc[tmp];
        rc[tmp] = T; sz[tmp] = sz[T];
        sz[T] = sz[lc[T]] + sz[rc[T]] + 1;
        T = tmp;
    }
    void Zag(int &T)
    {
        int tmp = rc[T]; rc[T] = lc[tmp];
        lc[tmp] = T; sz[tmp] = sz[T];
        sz[T] = sz[lc[T]] + sz[rc[T]] + 1;
        T = tmp;
    }
    void maintain(int &T, bool flag)
    {
        if (!T || !lc[T] && !rc[T]) return;
        if (!flag)
        {
            if (sz[lc[lc[T]]] > sz[rc[T]]) Zig(T);
            else if (sz[rc[lc[T]]] > sz[rc[T]])
            {Zag(lc[T]); Zig(T);}
            else return;
        }
        else
        {
            if (sz[rc[rc[T]]] > sz[lc[T]]) Zag(T);
            else if (sz[lc[rc[T]]] > sz[lc[T]])
            {Zig(rc[T]); Zag(T);}
            else return;
        }
        maintain(lc[T], 0); maintain(rc[T], 1);
        maintain(T, 0); maintain(T, 1);
    }
    void Ins(int &T, int v)
    {
        if (!T) {sz[T = ++tot] = 1; key[T] = v;}
        ++sz[T];
        if (v < key[T]) Ins(lc[T], v);
        else Ins(rc[T], v);
        maintain(T, v >= key[T]);
    }
    int Del(int &T, int v)
    {
        --sz[T];
        if (v == key[T]
            || v < key[T] && !lc[T]
            || v > key[T] && !rc[T])
        {
            int tmp = key[T];
            if (!lc[T] || !rc[T]) T = lc[T]+rc[T];
            else key[T] = Del(lc[T], key[T]);
            return tmp;
        }
        if (v < key[T]) return Del(lc[T], v);
        else return Del(rc[T], v);
    }
    int pred(int &T, int v, int ans)
    {
        if (!T) return ans;
        if (key[T]<v && key[T]>ans) ans=key[T];
        if (v<key[T]) return pred(lc[T], v, ans);
        else return pred(rc[T], v, ans);
    }
    int succ(int &T, int v, int ans)
    {
        if (!T) return ans;
        if (!(key[T]<v)&&key[T]<ans) ans=key[T];
        if (v<key[T]) return succ(lc[T], v, ans);
        else return succ(rc[T], v, ans);
    }
    int min(int T)
    {return lc[T] ? min(lc[T]) : key[T];}
    int max(int T)
    {return rc[T] ? max(rc[T]) : key[T];}
public:
    SBT(): tot(0), T(0)
    {
        memset(key, 0, sizeof key);
        memset(lc, 0, sizeof lc);
        memset(rc, 0, sizeof rc);
        memset(sz, 0, sizeof sz);
    }
    void Ins(int v) {Ins(T, v); return;}
    void Del(int v) {Del(T, v); return;}
    int pred(int v) {return pred(T, v, ~INF);}
    int succ(int v) {return succ(T, v, INF);}
    int min() {return min(T);}
    int max() {return max(T);}
};

3.最大流(SAP算法)(ditch)
int flow[maxN][maxN], d[maxN], cnt[maxN], n, m;
void readdata()
{
    scanf("%d%d", &m, &n); int u, v, e;
    for (int i = 0; i < m; ++i)
    {
        scanf("%d%d%d", &u, &v, &e);
        flow[u][v] += e;//从u到v可能有多条流量限制。
    }
}
int Sap(int x, int Lim)
{
    if (x == n) return Lim;
//若已经到了n点,则说明已经找到了增广路,返回流量。
    int tmp = 0;
    for (int i = 1; i < n + 1; ++i)
    if (flow[x][i] > 0 && d[x] == d[i] + 1)
    //从x节点的上一层寻找可增广路,若找到则更新当前流量。
    {
        int k=Sap(i, min(Lim-tmp, flow[x][i]));
        //递归找增广路。
        flow[x][i] -= k;
        flow[i][x] += k; //修改残量网络。
        if ((tmp += k) == Lim) return tmp;
        //若当前流量已经达到最大限度,则返回当前流量。
    }
    if (d[1] >= n) return tmp; //无法增广则返回。
    if ((--cnt[d[x]]) <= 0) d[1] = n;
    //出现断层则不再存在增广路。
    ++cnt[++d[x]];
//当前节点在第d[x]层已不存在增广路,则把x节点往上提。
    return tmp;
    //最后一定要返回当前的以求得的流量!!!!
}
//此模块表示从x节点到汇点的流量上限为Lim的增广路。
void work()
{
    int ans = 0; cnt[0] = n;
    while (d[1] < n) ans += Sap(1, MAX);
    //只要还可以增广就继续增广。
    printf("%d", ans);
}

4.最大匹配(匈牙利算法)(奶牛分配)
struct Edge {int dest;Edge *next;} *edge[maxN];
int Link[maxN], n, m, t, c, ans;
std::bitset <maxN> marked;
inline void insert(int u, int v)
{
    Edge *p = new Edge; p -> dest = v;
    p -> next = edge[u]; edge[u] = p;
}
void readdata()
{
    scanf("%d%d", &n, &m);
    for (int i = 1; i < n + 1; ++i)
    for (scanf("%d", &t);; t; --t)
    {scanf("%d", &c); insert(i, c);}
}
bool Find(int u)
{
    for (Edge *p = edge[u]; p; p = p -> next)
    {
        int v = p -> dest;
        if (marked[v]) continue;else marked[v]=1;
        if (!Link[v] || Find(Link[v]))
        {Link[v] = u; return true;}
    }
    return false;
}
void work()
{
    for (int i = 1; i < n + 1; ++i)
    {marked.reset(); if (Find(i)) ++ans;}
    printf("%d", ans);
}

5.最佳匹配(KM算法)(丘比特的烦恼)
struct vec
{
    int x, y; vec() {}
    vec(int x, int y): x(x), y(y) {}
    vec operator-(const vec &b) const
    {return vec(x - b.x, y - b.y);}
    int operator+(const vec &b) const
    {return x * b.x + y * b.y;}
    int operator*(const vec &b) const
    {return x * b.y - y * b.x;}
    int norm() const {return x * x + y * y;}
    bool btwn(const vec &A, const vec &B) const
    {
        vec OA = vec(A.x - x, A.y - y),
        OB = vec(B.x - x, B.y - y);
        return !(OA * OB) && OA + OB < 0;
    } 
};
map <string, int> _boy, _girl;
vec boy[maxN], girl[maxN];
bool b[maxN], g[maxN];
int lx[maxN], ly[maxN], Link[maxN];
int mp[maxN][maxN], n, K;
void readdata()
{
    cin >> K >> n;
    for (int i = 0; i < n; ++i)
    {
        int x, y; string str;
        cin >> x >> y >> str;
        boy[i] = vec(x, y);
        transform(str.begin(), str.end(),
                  str.begin(), ::tolower);
	//将一个string类型转换为小写。
	_boy[str] = i;
    }
    for (int i = 0; i < n; ++i)
    {
        int x, y; string str;
        cin >> x >> y >> str;
        girl[i] = vec(x, y);
        transform(str.begin(), str.end(),
                  str.begin(), ::tolower);
        _girl[str] = i;
        Link[i] = -1;
    }
    int w; string s1, s2;
    while (cin >> s1 >> s2 >> w)
    {
        transform(s1.begin(), s1.end(),
                  s1.begin(), ::tolower);
        transform(s2.begin(), s2.end(),
                  s2.begin(), ::tolower);
	    if (_boy.find(s1) == _boy.end())
            swap(s1, s2);
        mp[_boy[s1]][_girl[s2]] = w;
    }
}

void modify()
{
    for (int i = 0; i < n; ++i)
    for (int j = 0; j < n; ++j)
    {
        if (!mp[i][j]) mp[i][j] = 1;
        if ((boy[i] - girl[j]).norm() > K * K)
        {mp[i][j] = MIN; continue;}
        if (mp[i][j] > 0)
        for (int i1 = 0; i1 < n; ++i1)
        if (i1-i&&boy[i1].btwn(boy[i],girl[j]))
        {mp[i][j] = MIN; break;}
        if (mp[i][j] > 0)
        for (int j1 = 0; j1 < n; ++j1)
        if (j1-j&&girl[j1].btwn(boy[i],girl[j]))
        {mp[i][j] = MIN; break;}
        lx[i] = max(lx[i], mp[i][j]);
    } //当不能连边时一定要把权值设为负无穷。
}

bool Find(int i)
{
    b[i] = 1;
    for (int j = 0; j < n; ++j)
    if (lx[i] + ly[j] == mp[i][j] && !g[j])
    {
        g[j] = 1;
        if (Link[j] == -1 || Find(Link[j]))
        {Link[j] = i; return 1;}
    }
    return 0;
}

void work()
{
    for (int k = 0; k < n; ++k)
    while (1)
    {
        memset(b, 0, sizeof b);
        memset(g, 0, sizeof g);
        if (Find(k)) break; int Min = MAX;
        for (int i = 0; i < n; ++i) if (b[i])
        for (int j = 0; j < n; ++j) if (!g[j])
            Min = min(Min,lx[i]+ly[j]-mp[i][j]);
        for (int i = 0; i < n; ++i)
        {
            if (b[i]) lx[i] -= Min;
            if (g[i]) ly[i] += Min;
        }
    }
    int ans = 0;
    for (int j=0; j<n; ++j) ans+=mp[Link[j]][j];
    printf("%d\n", ans);
}

6.最小费用流(运输问题)
struct Edge
{int u, v, f_min, f_max, d; Edge *next, *back;};
Edge *edge[maxN], *pre[maxN];
bool marked[maxN];
int dist[maxN], q[SIZE + 1], n, m, S, T, f, r;
inline void Ins(int u, int v, int f, int d)
{
    Edge *p = new Edge;
    p -> u = u; p -> v = v; p -> d = d;
    p -> f_min = p -> f_max = f;
    p -> next = edge[u]; edge[u] = p;
    p = new Edge;
    p -> u = v; p -> v = u; p -> d = -d;
    p -> f_min = p -> f_max = 0;
    p -> next = edge[v]; edge[v] = p;
    edge[u] -> back = edge[v];
    edge[v] -> back = edge[u];
}
inline int min(int a,int b) {return a<b ? a:b;}
inline int max(int a,int b) {return a>b ? a:b;}

inline bool Spfa(int (*opt)(int, int))
{
    memset(dist,opt(0,1)?0xc0:0x3f,sizeof dist);
    memset(pre, 0, sizeof pre);
    memset(marked, 0, sizeof marked);
    dist[S] = 0; f=r=0; marked[q[r++] = S] = 1;
    while (f - r)
    {
        int u = q[f++]; f &= SIZE; marked[u] = 0;
        for (Edge *p = edge[u]; p; p = p -> next)
        if (opt(0, 1) ? p -> f_max : p -> f_min)
        {
            int v= p -> v,
            tmp = opt(dist[v], dist[u] + p -> d);
            if (tmp - dist[v])
            {
                dist[v] = tmp; pre[v] = p;
                if (!marked[v])
                {marked[q[r++] = v] = 1; r&=SIZE;}
            }
        }
    }
    return (bool)pre[T];
}
int main()
{
    n = getint(); m = getint();
    S = n + m + 1; T = n + m + 2;
    for (int i = 1; i < n + 1; ++i)
        Ins(S, i, getint(), 0);
    for (int j = 1; j < m + 1; ++j)
        Ins(j + n, T, getint(), 0);
    for (int i = 1; i < n + 1; ++i)
    for (int j = 1; j < m + 1; ++j)
        Ins(i, j + n, MAX, getint());
    int Min = 0, Max = 0;
    while (Spfa(min))
    {
        int Max_flow = MAX;
        for (Edge *p = pre[T]; p; p = pre[p->u])
            Max_flow = min(Max_flow, p -> f_min);
        for (Edge *p = pre[T]; p; p = pre[p->u])
        {
            p -> f_min -= Max_flow;
            p -> back -> f_min += Max_flow;
        }
        Min += Max_flow * dist[T];
    }
    while (Spfa(max))
    {
        int Max_flow = MAX;
        for (Edge *p = pre[T]; p; p = pre[p->u])
            Max_flow = min(Max_flow, p -> f_max);
        for (Edge *p = pre[T]; p; p = pre[p->u])
        {
            p -> f_max -= Max_flow;
            p -> back -> f_max += Max_flow;
        }
        Max += Max_flow * dist[T];
    }
    printf("%d\n%d\n", Min, Max); return 0;
}

7.凸包(Graham算法)(Wall)
typedef complex <double> Point;
Point p[maxN], res[maxN]; int n, R, top;
void readdata()
{
    scanf("%d%d", &n, &R);
    for (int i = 0; i < n; ++i)
    {
        double x, y; scanf("%lf%lf", &x, &y);
        p[i] = Point(x, y);
    }
}
template <typename _Tp>
_Tp outer_product(const complex <_Tp> a,
                     const complex <_Tp> b)
{return a.real()*b.imag()-a.imag()*b.real();}

int cmp(const void *a, const void *b)
{
    Point A = *(Point *)a, B = *(Point *)b;
    if (A.imag() < B.imag() - zero) return -1;
    if (A.imag() > B.imag() + zero) return 1;
    if (A.real() < B.real() - zero) return -1;
    if (A.real() > B.real() + zero) return 1;
    return 0;
}
void work()
{
    qsort(p, n, sizeof(p[0]), cmp);
    res[0] = p[0]; res[1] = p[1]; top = 1;
    for (int i = 2; i < n; ++i)
    {
        while (top&&outer_product(p[i]-res[top],
               res[top]-res[top-1])>-zero)
            --top;
        res[++top] = p[i];
    }
    int tmp = top; res[++top] = p[n - 2];
    for (int i = n - 3; i > -1; --i)
    {
        while (top > tmp &&
               outer_product(p[i] - res[top],
               res[top] - res[top - 1]) > -zero)
            --top;
        res[++top] = p[i];
    }
    double ans=Pi*(R<<1)+abs(res[0]-res[top-1]);
    for (int i = 1; i < top; ++i)
        ans += abs(res[i] - res[i - 1]);
    printf("%d\n", (int)(ans + 0.5));
}

8.RMQ问题
void rmq()
{
    for (int i = 1; i <= n; ++i) f[i][0] = a[i];
    for (int q = 0; 1 << q < n; ++q)
    for (int i = 1; i + (1 << q) < n + 2; ++i)
        f[i][q+1] = max(f[i+(1<<q)][q],f[i][q]);
}
int query(int L, int R)
{
    int q = 0; for (; 1<<q < R-L+2; ++q); --q;
    return max(f[L][q], f[R - (1<<q) + 1][q]);
}

9.无向图找桥和格点(danger)
struct Edge {int dest; Edge *next;}*edge[maxN];
bool bridge[maxN][maxN], marked[maxN];
int low[maxN], D[maxN], n, m, deep;
void insert(int u, int v)
{
    Edge *p = new Edge;  p -> dest = v;
    p -> next = edge[u]; edge[u] = p;
}
void readdata()
{
    cin >> n >> m;
    for (int i = 1; i <= m; i++)
    {
        int u, v; cin >> u >> v;
        insert(u, v); insert(v, u);
    }
}
void DFS(int u, int x)
{
    D[u] = low[u] = ++deep; marked[u] = true;
    for (Edge *p = edge[u]; p; p = p -> next)
    {
        int v = p -> dest;
        if (v != x && !marked[v]) DFS(v, u);
//若v不是u的父节点,且v未被遍历过,则遍历v。
        if (v != x && marked[v])
        {
            low[u] = min(low[u], D[v]);
            low[u] = min(low[u], low[v]);
            //以上两行更新low[u]的值。
            if (D[u] < low[v])
                bridge[u][v] = bridge[v][u] = 1;
            //标记(u, v)为桥。
        }
    }
} //对节点u进行遍历。(其中,x为u的父节点。)
void print()
{
    for (int i = 1; i <= n; i++)
    for (int j = i + 1; j <= n; j++)
        if (bridge[i][j]) cout<<i<<" "<<j<<endl;
}
void work()
{
    deep = 0; //DFS序号初始化为0。
    DFS(1, 0);
//从1开始(任意一个节点开始都可以)遍历,无父节点。
    print();
}

无向图找桥:

对图深度优先搜索,定义D[u]为u在搜索树(以下简称为树)中被遍历到的次序号。
定义low[u]为u或u的子树中能通过非父子边追溯到的最早的节点,即DFS序号最小的节点。

根据定义,则有:
Low(u)=min{D[u], D[v], low[v]}
对于D[v]:(u,v)为后向边(返祖边、回边) 。
(等价于 DFS(v)<DFS(u)且v不为u的父亲节点。)
对于low[v]:(u,v)为树枝边(父子边,即v为u的子节点)。

一个顶点u是割点,当且仅当满足(1)或(2)
(1) u为树根,且u有多于一个子树。
(2) u不为树根,且满足存在(u,v)为树枝边(或称父子边,
即u为v在搜索树中的父亲),使得D[u] <= low[v]。

一条无向边(u,v)是桥,当且仅当(u,v)为树枝边,且满足D[u] < low[v]。

10.强连通分量(Popular Cow's)
struct Edge {int v;Edge *next;};
Edge *edge[maxN]; bool marked[maxN];
int stack[maxN], belong[maxN], out[maxN];
int DFN[maxN], Low[maxN], n, top, Bcnt, Index;
inline void insert(int u, int v)
{
    Edge *p = new Edge; p -> v = v;
    p -> next = edge[u]; edge[u] = p;
}
void tarjan(int u)
{
    DFN[u] = Low[u] = ++Index;
    marked[stack[++top] = u] = true;
    for (Edge *p = edge[u]; p; p = p -> next)
    {
        int v = p -> v;
        if (!DFN[v])
        {
            tarjan(v);
            if (Low[v] < Low[u]) Low[u] = Low[v];
        }
        else if (marked[v] && DFN[v] < Low[u])
            Low[u] = DFN[v];
    }
    if (DFN[u] == Low[u])
    {
        ++Bcnt; int tmp = u;
        do
        {
            tmp = stack[top--];
            marked[tmp] = false;
            belong[tmp] = Bcnt;
        } while (tmp != u);
    }
}
int work()
{
    for (int i = 1; i < n + 1; ++i)
        if (!DFN[i]) tarjan(i);
    for (int i = 1; i < n + 1; ++i)
    for (Edge *p = edge[i]; p; p = p -> next)
    if (belong[i] != belong[p -> v])
        ++out[belong[i]];
    int pos = 0;
    for (int i = 1; i < Bcnt + 1; ++i)
    if (!out[i]) if (pos) return 0; else pos=i;
    int ans = 0;
    for (int i = 1; i < n + 1; ++i)
    if (belong[i] == pos) ++ans;
    return ans;
}
int main()
{
    n = getint(); int m = getint();
    for (; m; --m)
    {int u=getint(), v=getint(); insert(u, v);}
    printf("%d\n", work()); return 0;
}
Tarjan算法:
找出所有的强连通分量,分别统计每个强连通分量的点的个数和出度,若出度为0的强连通分量数为1,则答案等于此强连通分量的点的个数,否则答案为零。

11.最近公共祖先(向RMQ转化)(Tree)
#pragma comment(linker, "/STACK:0x10000000")
#define RMQ_min(a, b) (D[a] < D[b] ? (a) : (b))
struct Edge {int v, d; Edge *next;};
Edge *edge[maxN];
int D[maxORD], ord[maxORD], f[maxORD][20];
int fir[maxN], dist[maxN], Ind;
void Dfs(int u, int Last, int Dep)
{
    ord[++Ind] = u; fir[u] = Ind; D[Ind] = Dep;
    for (Edge *p = edge[u]; p; p = p -> next)
    if (p -> v - Last)
    {
        dist[p -> v] = dist[u] + p -> d;
        Dfs(p -> v, u, Dep + 1);
        ord[++Ind] = u; D[Ind] = Dep;
    }
}
inline void RMQ_set()
{
    for (int i = 1; i <= Ind; ++i) f[i][0] = i;
    for (int q = 0; 1 << q < Ind; ++q)
    for (int i = 1; i + (1 << q) < Ind + 1; ++i)
      f[i][q+1]=RMQ_min(f[i][q],f[i+(1<<q)][q]);
}
inline int RMQ(int L, int R)
{
    if(L==R) return L; if(L>R) std::swap(L,R);
    int q = 0; for (; 1<<q < R-L+2; ++q); --q;
    return RMQ_min(f[L][q], f[R-(1<<q)+1][q]);
}
inline void Ins(int u, int v, int d)
{
    Edge *p = new Edge; p -> v = v;
    p -> d = d; p -> next = edge[u];
    edge[u] = p;
}
int main()
{
    for (int n = getint(); --n;)
    {
        int u=getint(), v=getint(), d=getint();
        Ins(u, v, d); Ins(v, u, d);
    }
    Dfs(0, -1, 0); RMQ_set();
    for (int m = getint(); m; --m)
    {
        int u = getint(), v = getint();
        printf("%d\n", dist[u] + dist[v] -
          (dist[ord[RMQ(fir[u], fir[v])]]<<1));
    }
    return 0;
}
#undef RMQ_min

12.AC自动机(Keywords Search)
struct AC_auto
{
    AC_auto *next[26], *Fail;
//next指针即为各个字母(共26个字母);当匹配不
//成功时,Fail指针用于跳转到Trie树中的另一节点。
    int cnt;
     AC_auto(): Fail(NULL), cnt(0)
    {memset(next, 0, sizeof next);}
};
AC_auto *q[SIZE + 1]; //用队列辅助建立AC自动机。
char key[maxL], str[maxLEN];
int n, T, f, r;
inline void insert(AC_auto*&root,const char*str)
{
    AC_auto *p = root;
    for (int i = 0; str[i]; ++i)
    {
        int Index = str[i] - 'a';
        if (!(p -> next[Index]))
            p -> next[Index] = new AC_auto();
        p = p -> next[Index];
    }
    ++(p -> cnt); //用于记录到此为止的单词数目。
}
//将字符串str插入到Trie树中,这棵树从根到叶(也可能
//是到中间的某个字母)的每一条路径上的字母为一个单词。
inline void build(AC_auto *&root)
{
    root -> Fail = NULL; //根节点的失败指针为空。
    f = r = 0;
    for (q[r++] = root; f < r;)
    {
        AC_auto *Now = q[f++], *p = NULL;
        for (int i=0;i<26;++i) if (Now->next[i])   
    //若当前节点有这棵子树,则遍历。
        {
            if (Now == root)
                Now -> next[i] -> Fail = root;
	//根的各个直接子节点的失败指针为根。
            else for (p=Now->Fail; p; p=p->Fail)
            if (p -> next[i])
            {
                Now->next[i]->Fail = p->next[i];
                break;
            }
	//找当前节点的其它失败指针中是否存在该字母,
	//若有,则把当前节点的子树的失败指针指向之。
            if (!p) Now->next[i]->Fail = root;
	//若没找到,则失败指针为根。
            q[r++]=Now->next[i];//将这棵子树入队。
        }
    }
}
inline int query(AC_auto *root, const char *str)
{
    int cnt = 0;
    AC_auto *p = root;
    for (int i = 0; str[i]; ++i)
    {
        int Index = str[i] - 'a';
        while (!(p -> next[Index]) && p != root)
            p = p -> Fail;
	//此路不通时,找它的失败指针。
        p = p -> next[Index];
        p = p ? p : root;
        for (AC_auto *tmp = p; tmp != root &&
             tmp -> cnt != -1; tmp = tmp->Fail)
        {
            cnt += tmp -> cnt;
            tmp -> cnt = -1; //防止多次统计。
        } //顺便统计其他失败指针中的单词个数。
    }
    return cnt;
}
int main()
{
    scanf("%d", &T);
    for (; T; --T)
    {
        scanf("%d", &n);
        AC_auto *root = new AC_auto();
        for (; n; --n)
        {scanf("%s", key); insert(root, key);}
        build(root); scanf("%s", str);
        printf("%d\n", query(root, str));
    }
    return 0;
}

13.左偏树(Financial Fraud)
struct LeftList
{
    int key, dist; LeftList *lc, *rc;
    LeftList() {}
    LeftList(int key):
         key(key), dist(0), lc(NULL), rc(NULL) {}
};
LeftList *tr[maxN];
int L[maxN], R[maxN], sz[maxN], a[maxN];
bool cmp(const LeftList *a,const LeftList *b)
{return a -> key > b -> key;}
//默认为小根,这里重载比较为大根。
LeftList *Merge(LeftList *a, LeftList *b)
{
    if (!a) return b; if (!b) return a;
//待合并的两棵子树中,若其中一棵为空,则直接返回另一棵树。
    if (cmp(b, a)) std::swap(a, b);
	//为了方便操作,让根值较大(大根树)
	//或较小(小根树)的树作为合并后的根。
    a -> rc = Merge(a -> rc, b);
    if (!(a -> lc) || a -> rc &&
        a -> rc -> dist > a -> lc -> dist)
        std::swap(a -> lc, a -> rc);
	//调整左右子树的位置使其满足左偏树的性质。
	//(左子树的距离不小于右子树的距离即左偏的性质。)
    if (!(a -> rc)) a -> dist = 0;
    else a -> dist = a -> rc -> dist + 1;
	//更新合并后的树的距离。
    return a;
}
inline void Del(LeftList *&a)
//这里引用符号“&”不能省。
{a = Merge(a -> lc, a -> rc);}
int main()
{
    int n;
    while (n = getint())
    {
        int top = 0;
        for (int i = 0; i < n; ++i)
        {
            tr[++top]= new
                   LeftList(a[i] = getint());
            sz[top] = 1; L[top] = R[top] = i;
		//用L,R指针记录当前左偏树所代表的区间。
            while (top > 1 && tr[top - 1] -> key
                   > tr[top] -> key)
            {
                tr[top-1] = Merge(tr[top – 1],
                                         tr[top]);
                sz[top - 1] += sz[top];
                R[top - 1] = R[top];
                for (--top; sz[top]>(R[top]
                         -L[top]>>1)+1;--sz[top])
                    Del(tr[top]);
//使得合并后的左偏树的根节点(即最大值)不超过该区间的中
//位数,即让左偏树中总节点数不超过该区间中总结点数的一半
            }
        }
        long long ans = 0;
        for (int i = 1; i < top + 1; ++i)
        for (int j = L[i]; j < R[i] + 1; ++j)
            ans += abs(a[j] - tr[i] -> key);
        printf("%lld\n", ans);
    }
    return 0;
}
#undef abs

14.树状数组(Stars)
#define lowbit(x) ((x) & (-(x)))
int a[maxX], lev[maxN], n;
inline void Ins(int x)
{for (int i=x; i<maxX; i+=lowbit(i)) ++a[i];}

inline int Sum(int x)
{
    int ans = 0;
    for (int i=x; i; i-=lowbit(i)) ans += a[i];
    return ans;
}
int main()
{
    n = getint();
    for (int i = 0; i < n; ++i)
    {
        int x = getint() + 1, y = getint() + 1;
        ++lev[Sum(x)]; Ins(x);
    }
    for (int i=0; i<n; ++i)
        printf("%d\n", lev[i]);
    return 0;
}
#undef lowbit


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值