板子3

最小树形图(朱刘算法)

int pre[maxn], col[maxn], id[maxn], in[maxn];

struct Edge {
    int u, v, va;
} e[maxn*40];

int ZL_MST(int root, int nv, int tot) {
    int rtans = 0;
    while (1) {
        for (int i = 1; i <= nv; i++) in[i] = INF;
        for (int i = 1; i <= tot; i++) {
            int u = e[i].u, v = e[i].v;
            if (u != v && e[i].va < in[v]) {
                pre[v] = u;
                in[v] = e[i].va;
            }
        }
        for (int i = 1; i <= nv; i++) {
            if (i != root && in[i] == INF)
                return -1;
        }
        int cnt = 0; in[root] = 0;
        memset(col, -1, sizeof(col));
        memset(id, -1, sizeof(id));
        for (int i = 1; i <= nv; i++) {
            rtans += in[i];
            int v = i;
            while (col[v] != i && id[v] == -1 && v != root) {
                col[v] = i;
                v = pre[v];
            }
            if (id[v] == -1 && v != root) {
                id[v] = ++cnt;
                for (int j = pre[v]; j != v; j = pre[j]) id[j] = cnt;
            }
        }
        if (!cnt) break;
        for (int i = 1; i <= nv; i++) if (id[i] == -1) id[i] = ++cnt;
        for (int i = 1; i <= tot; i++) {
            int v = e[i].v;
            e[i].u = id[e[i].u];
            e[i].v = id[e[i].v];
            if (e[i].u != e[i].v)
                e[i].va -= in[v];
        }
        nv = cnt;
        root = id[root];
    }
    return rtans;
}

 辗转相除的搞死小圆(带计算生成树计数)

ll g[maxn][maxn];

ll Ma_Tree(int len) {
    ll ans = 1;
    for (int i = 1; i <= len; i++) {
    	for (int j = 1; j <= len; j++) {
    		g[i][j] = (g[i][j] + mod) % mod;
    	}
    }
    for (int i = 1; i <= len; i++) {
        for (int j = i + 1; j <= len; j++) {
            ll a = g[i][i], b = g[j][i];
            while (b) {
                ll tmp = a / b; a %= b; swap(a, b);
                for (int k = i; k <= len; k++) g[i][k] = (g[i][k] - g[j][k]*tmp + mod) % mod;
                for (int k = i; k <= len; k++) swap(g[i][k], g[j][k]);
                ans = -ans;
            }
        }
    }
    for (int i = 1; i <= len; i++) {
    	//if (g[i][i] == 0) return -1;
        ans = ans * g[i][i] % mod;
    }
    if (ans < 0) ans += mod;
    return ans;
}

 

用SPFA解需要注意的:

1.原图可能不是连通图,故需要加一个超级源点S,从S到任意的顶点边权为0,然后从该点出发。为什么?添加从虚点S到每个顶点的权为0的边.这是为了保证构造出来的图是连通的.由于虚点本身并不引入负圈,所以设置虚点以后最短路仍然存在,并且每个约束仍然满足.

或者差分约束不用什么附加顶点, 附加顶点的唯一用处就是保证图的连通性, 不让你有负环判不到的情况, 解决这种问题的最佳途径就是初始把所有顶点都加入队列, 并且将所有dis

置0, 这就相当于加了一个不存在的附加顶点, 它与所有的顶点的直连长度都是0.

当然推荐第二种,效率也高。


2.如果求最小的解,那么我们一开始把dis设为无穷小,并且使用最长路。即 if(d[v] < d[u]+w(u,v))    进行更新,而建图的时候也要用大于等于。

如果求最大,那么我们一开始把dis设为无穷大,使用最短路。

为什么?

问得好!(- -|||) 以求解最大的为例(最小解同理)dis[id]一开始为无穷大,图最短路更新的条件为: if(d[v]>d[u]+w(u,v))   d[v]=d[u]+w(u,v); 通过不断的松弛,使得d的值不断变小,直到满足所有条件,也就是说满足条件的时候就是最大的了~

那么,我们建图的时,都转化为A-B<=C这种形式,以(B为起点,向A连接一条权值为C的边) 因为图的最短路有:d[v]- d[u]<=w(u,v); 即( d[v]<=d[u]+w(u,v); )


所以,当你在纠结用小于号大于号的时候,看看题目求的是最大还是最小,如果只是判断有木有解,那么大于号小于都可以,只不过要注意全部等式要统一。

 

差分约束系统(SPFA)(包括答案,都化成a - b <= c的形式)

int head[maxn], cnt, dis[maxn];
struct Edge {
    int v, w, next;
} e[maxn*20];

void add(int u, int v, int w) {
    e[cnt].v = v;
    e[cnt].w = w;
    e[cnt].next = head[u];
    head[u] = cnt++;
}

void init() {
    cnt = 0;
    memset(head, -1, sizeof(head));
}

bool spfa(int s, int len) {
    bool vis[maxn] = {0};
    int in[maxn] = {0};
    queue<int> q;
    for (int i = 1; i <= len; i++)
        dis[i] = INF;
    q.push(s);
    vis[s] = true;
    dis[s] = 0;
    while (!q.empty()) {
        int t = q.front(); q.pop();
        vis[t] = false;
        for (int i = head[t]; ~i; i = e[i].next) {
            int v = e[i].v;
            if (dis[v] > dis[t] + e[i].w) {
                dis[v] = dis[t] + e[i].w;
                if (!vis[v]) {
                    q.push(v);
                    vis[v] = true;
                    if (++in[v] > len) return false;
                }
            }
        }
    }
    return true;
}
// 其中a - c <= ? 是求最短路
void solve() {
    int n, x, y;

    cin >> n >> x >> y;
    init();
    for (int i = 1; i <= x; i++) {
        int a, b, c; scanf("%d%d%d", &a, &b, &c);
        add(a, b, c);  //b - a <= c
    }
    for (int i = 1; i <= y; i++) {
        int a, b, c; scanf("%d%d%d", &a, &b, &c);
        add(b, a, -c);  // b - a >= c   => a - b <= -c
    }
    if (!spfa(1, n)) cout << -1 << endl; //有环是无解
    else {
        if (dis[n] == INF) cout << -2 << endl;  //到不了是多解,表示a和c没有关系
        else cout << dis[n] << endl;
    }
}

  刘汝佳的Dinic(很快~)

int head[maxn], cnt;
int n, m, s, t;
bool vis[maxn];
int d[maxn], cur[maxn];
struct Edge {
    int u, v;
    int cap, flow;
} e[maxn*40]; //因为是双向边  所以记得开二倍

vector<int> G[maxn];

void init() {
    memset(head, -1, sizeof(head));
    for (int i = 0; i <= 200; i++) //一定不要对vector使用memset!!!!!
        G[i].clear();
    cnt = 0;
}

void add(int u, int v, int cap, int f) {
    e[cnt].u = u;
    e[cnt].cap = cap;
    e[cnt].flow = f;
    e[cnt].v = v;
}

void AddEdge(int u, int v, int cap) {
    add(u, v, cap, 0);
    G[u].push_back(cnt++);
    add(v, u, 0, 0);
    G[v].push_back(cnt++);
}

bool BFS() {
    memset(vis, 0, sizeof(vis));
    queue<int> q;
    q.push(s);
    vis[s] = 1;
    d[s] = 0;
    while (!q.empty()) {
        int v = q.front(); q.pop();
        for (int i = 0; i < G[v].size(); i++) {
            Edge &te = e[G[v][i]];
            if (!vis[te.v] && te.cap > te.flow) { //只考虑残量网络的弧
                vis[te.v] = 1;
                d[te.v] = d[v] + 1;
                q.push(te.v);
            }
        }
    }
    return vis[t];
}

int dfs(int x, int a) {
    if (x == t || a == 0) return a;
    int flow = 0, f;
    for (int &i = cur[x]; i < G[x].size(); i++) { //从上次考虑的弧
        Edge &te = e[G[x][i]];
        if (d[x] + 1 == d[te.v] && (f = dfs(te.v, min(a, te.cap - te.flow))) > 0) {
            te.flow += f;
            e[G[x][i]^1].flow -= f;
            flow += f;
            a -= f;
            if (a == 0) break;
        }
    }
    return flow;
}

int Dinic() {
    int flow = 0;
    while (BFS()) {
        memset(cur, 0, sizeof(cur));
        flow += dfs(s, INF);
    }
    return flow;
}

  静态Trie

const int maxnNode = 1e6 + 5;
int ch[maxnNode][26];
int val[maxnNode];

struct Trie {
    int sz;
    Trie() {
        sz = 1; memset(val, 0, sizeof(val));
        memset(ch[0], 0, sizeof(ch[0]));
    }
    int idx(char c) { return c - 'a'; }
    void Insert(char *s, int v) {
        int u = 0, n = strlen(s);
        for (int i = 0; i < n; i++) {
            int c = idx(s[i]);
            if (!ch[u][c]) {
                memset(ch[sz], 0, sizeof(ch[sz]));
                val[sz] = 0;
                ch[u][c] = sz++;
            }
            u = ch[u][c];
            val[u] += v;
        }
        //字符串的字符的附加信息
    }
    int Query(char *s) {
        int u = 0, n = strlen(s);
        for (int i = 0; i < n; i++) {
            int c = idx(s[i]);
            if (!ch[u][c]) return 0;
            u = ch[u][c];
        }
        return val[u];
    }
};

  区间线段树

struct Edge {
    int v, next;
} e[maxn<<1];

struct TREE{
    int l, r; ll val, maxx, lazy;
    void fun(ll tmp) {		//如果是直接赋值就用 = 
        lazy += tmp;
        val += (r - l + 1) * tmp;
        maxx += tmp;
    }
} tre[maxn*4];

void PushDown(int id) {
    if (tre[id].lazy) {
        tre[id<<1].fun(tre[id].lazy);
        tre[id<<1|1].fun(tre[id].lazy);
        tre[id].lazy = 0;
    }
}

void PushUp(int id) {
    tre[id].val = tre[id<<1].val + tre[id<<1|1].val;
    tre[id].maxx = max(tre[id<<1].maxx, tre[id<<1|1].maxx);
}

void build(int id, int l, int r) {
    tre[id].l = l, tre[id].r = r, tre[id].lazy = 0;
    if (l == r) tre[id].val = tre[id].maxx = dis[idx[l]];
    else {
        int mid = (l+r) >> 1;
        build(id<<1, l, mid);
        build(id<<1|1, mid+1, r);
        PushUp(id);
    }
}

void update(int id, int st, int ed, int val) {
    int l = tre[id].l, r = tre[id].r;
    if (st <= l && ed >= r) tre[id].fun(val);
    else {
        PushDown(id);
        int mid = (l+r) >> 1;
        if (st <= mid) update(id<<1, st, ed, val);
        if (ed > mid) update(id<<1|1, st, ed, val);
        PushUp(id);
    }
}

ll query(int id, int st, int ed) {
    int l = tre[id].l, r = tre[id].r;
    if (st <= l && ed >= r) return tre[id].maxx;
    else {
        PushDown(id);
        int mid = (l+r) >> 1;
        ll sum1 = -inf, sum2 = -inf;
        if (st <= mid) sum1 = query(id<<1, st, ed);
        if (ed > mid) sum2 = query(id<<1|1, st, ed);
        PushUp(id);
        return max(sum1, sum2);
    }
}

  01字典树(XOR最大值)

const int maxnNode = 32*maxn;
int ch[maxnNode][2];
ll val[maxnNode];
int num[maxnNode];
int sz;
//因为是32   所以记得开long long
void init() {
    memset(ch[0], 0, sizeof(ch[0]));
    sz = 1;
}
//对于一个从来没有出现过的数一定要先插入  这样才有编号
void Insert(ll a, int d) {
    int u = 0;
    for (int i = 32; i >= 0; i--) {
        int c = ((a>>i) & 1);
        if (!ch[u][c]) {
            memset(ch[sz], 0, sizeof(ch[0]));
            num[sz] = 0;
            val[sz] = 0;
            ch[u][c] = sz++;
        }
        u = ch[u][c];
        num[u] += d;    //记录当前数出现几次
    }
    val[u] = a; //记录当前数的值
}

void update(ll a, int d) {
    int u = 0;
    for (int i = 32; i >= 0; i--) {
        int c = ((a>>i) & 1);
        u = ch[u][c];
        num[u] += d;
    }
}

ll query(ll a) {
    int u = 0;
    for (int i = 32; i >= 0; i--) {
        int c = ((a>>i) & 1);
        if (ch[u][c^1] && num[ch[u][c^1]]) u = ch[u][c^1];
        else u = ch[u][c];
    }
    return a ^ val[u];
}
int a[maxn];ll sum[maxn];

void solve() {
    int n;

    while (cin >> n) {
        init();
        Insert(0, 1);   //记得插入0
        for (int i = 1; i <= n; i++) {
            scanf("%d", a + i);
        }
        int tot = 0; sum[0] = 0;
        for (int i = 1; i <= n; i++) {
            sum[i] = max(sum[i-1], query(a[i]));
            tot ^= a[i];
            Insert(tot, 1);
        }
        init(); Insert(0, 1);
        tot = 0;
        ll ans = 0;
        for (int i = n; i >= 2; i--) {
            ll tmp = query(a[i]);
            ans = max(ans, tmp + sum[i-1]);
            tot ^= a[i];
            Insert(tot, 1);
        }
        cout << ans << endl;
    }
} 

  树链剖分(注意树链剖分也是满足dfs序列的)

int n, head[maxn], cnt, tim;    //tim是节点的编号
int fa[maxn], top[maxn], dep[maxn];     //top是链的最高的节点
int siz[maxn], out[maxn], son[maxn], id[maxn], a[maxn], pos[maxn]; //id是节点的编号,pos是编号的节点

struct Edge {
    int v, next;
} e[maxn<<1];

struct TREE{
    int l, r; int val, maxx, lazy;
    void fun(int tmp) {      //如果是直接赋值就用 =
        lazy = tmp;
        val = (r - l + 1) * tmp;
        maxx = tmp;
    }
} tre[maxn*4];

struct An {
    int maxx, sum;
};

void PushDown(int id) {
    if (tre[id].lazy) {
        tre[id<<1].fun(tre[id].lazy);
        tre[id<<1|1].fun(tre[id].lazy);
        tre[id].lazy = 0;
    }
}

void PushUp(int id) {
    tre[id].val = tre[id<<1].val + tre[id<<1|1].val;
    tre[id].maxx = max(tre[id<<1].maxx, tre[id<<1|1].maxx);
}

void build(int id, int l, int r) {
    tre[id].l = l, tre[id].r = r, tre[id].lazy = 0;
    if (l == r) tre[id].val = tre[id].maxx = a[pos[l]];
    else {
        int mid = (l+r) >> 1;
        build(id<<1, l, mid);
        build(id<<1|1, mid+1, r);
        PushUp(id);
    }
}

void update(int id, int st, int ed, int val) {
    int l = tre[id].l, r = tre[id].r;
    if (st <= l && ed >= r) tre[id].fun(val);
    else {
        PushDown(id);
        int mid = (l+r) >> 1;
        if (st <= mid) update(id<<1, st, ed, val);
        if (ed > mid) update(id<<1|1, st, ed, val);
        PushUp(id);
    }
}

int query_sum(int id, int st, int ed) { //f -> sum
    int l = tre[id].l, r = tre[id].r;
    if (st <= l && ed >= r) {
        return tre[id].val;
    }
    else {
        PushDown(id);
        int mid = (l+r) >> 1;
        int tmp1 = 0, tmp2 = 0;
        if (st <= mid) tmp1 = query_sum(id<<1, st, ed);
        if (ed > mid) tmp2 = query_sum(id<<1|1, st, ed);
        PushUp(id);
        return tmp1 + tmp2;
    }
}

int query_max(int id, int st, int ed) { //f -> sum
    int l = tre[id].l, r = tre[id].r;
    if (st <= l && ed >= r) {
        return tre[id].maxx;
    }
    else {
        PushDown(id);
        int mid = (l+r) >> 1;
        int tmp1 = -INF, tmp2 = -INF;
        if (st <= mid) tmp1 = query_max(id<<1, st, ed);
        if (ed > mid) tmp2 = query_max(id<<1|1, st, ed);
        PushUp(id);
        return max(tmp1, tmp2);
    }
}

void init() {
    tim = cnt = 0;
    memset(head, -1, sizeof(head));
    memset(son, -1, sizeof(son));
}

void add(int u, int v) {
    e[cnt].v = v;
    e[cnt].next = head[u];
    head[u] = cnt++;
}

void dfs1(int u, int deep) {
    dep[u] = deep; siz[u] = 1;
    for (int i = head[u]; ~i; i = e[i].next) {
        int v = e[i].v;
        if (v == fa[u]) continue;
        fa[v] = u;
        dfs1(v, deep + 1);
        siz[u] += siz[v];
        if (son[u] == -1 || siz[son[u]] < siz[v])
            son[u] = v;
    }
}

void dfs2(int u, int tp) {
    top[u] = tp;
    id[u] = ++tim;
    pos[tim] = u;
    if (son[u] == -1) {
        out[u] = tim;//叶子节点必须要在这里写
        return;
    }
    dfs2(son[u], tp);
    for (int i = head[u]; ~i; i = e[i].next) {
        int v = e[i].v;
        if (v != son[u] && v != fa[u])
            dfs2(v, v);
    }
    out[u] = tim;
}

int Find(int u, int v, int f) { // f != 0 代表求和, f == 0代表求最值
    int tp1 = top[u], tp2 = top[v];
    int tmp = -INF;
    if (f) tmp = 0;
    while (tp1 != tp2) {
        if (dep[tp1] < dep[tp2]) {      //从u开始跳  保证u最低
            swap(tp1, tp2); swap(u, v);
        }
        if (f) tmp += query_sum(1, id[tp1], id[u]);
        else tmp = max(tmp, query_max(1, id[tp1], id[u]));
        u = fa[tp1]; tp1 = top[u];
    }
    if (dep[u] < dep[v]) swap(u, v);
    if (f) tmp += query_sum(1, id[v], id[u]);
    else tmp = max(tmp, query_max(1, id[v], id[u]));    //同一条链上的越上面的节点编号越小
    return tmp;
}

void solve() {
    while (cin >> n) {
        init();
        for (int i = 1; i < n; i++) {
            int u, v; scanf("%d%d", &u, &v);
            add(u, v); add(v, u);
        }
        for (int i = 1; i <= n; i++)
            scanf("%d", a + i);
        dfs1(1, 0);
        dfs2(1, 1);
        build(1, 1, n);
        int q; cin >> q;
        while (q--) {
            char op[10]; int u, v;
            scanf("%s%d%d", op, &u, &v);
            if (op[0] == 'Q') {
                if (op[1] == 'M') {
                    printf("%d\n", Find(u, v, 0));
                }
                else {
                    printf("%d\n", Find(u, v, 1));
                }
            }
            else {
                update(1, id[u], id[u], v);
            }
        }
    }
}

  

  

 莫队:

离线的思想,但是对于一些刁钻的数据,离线的复杂度不是很理想,所以就用到分块的思想,将每

一块的左端点的间隔设置到了最多sqrt(n)的间距,这样就能使复杂度降低。感觉写端点移动的时候比较烦。

  排序地方的代码:

   bool cmp1(const Query &a, const Query &b) {
       if (pos[a.l] == pos[b.l])
           return a.r < b.r;
       return pos[a.l] < pos[b.l];     //以分块为第一关键字
   }
   bool cmp2(const Query &a, const Query &b) {
       return a.id < b.id; //按顺序输出
   }
分块的:

       int limit = sqrt((db)n + 0.5);  //每一块的大小
       for (int i = 1; i <= n; i++)
           pos[i] = (i-1) / limit + 1;     
      //分块,我也不知道为啥要(i-1) / limit + 1,我觉得直接 i / limit就可以了呀 for (int i = 1; i <= m; i++) { scanf("%lld%lld", &q[i].l, &q[i].r); q[i].id = i; } 移动的: while (l < q[i].l) { sum -= (f[a[l]] << 1) - 1; f[a[l]]--; l++; } while (l > q[i].l) { l--; sum += (f[a[l]] << 1) + 1; f[a[l]]++; } while (r < q[i].r) { r++; sum += (f[a[r]] << 1) + 1; f[a[r]]++; } while (r > q[i].r) { sum -= (f[a[r]] << 1) - 1; f[a[r]]--; r--; }

  hash:

使用 hash[i]=(hash[i-1]*p+idx(s[i]))%mod 求得前缀为i的hash值,利用 hash[l..r]=(hash[r]-hash[l-1]*(p^(r-1+1)))%mod 求得s[l,r]的hash值.(注意l=0的问题,以及hash[l..r] < 0时要 +mod)

 

 

代码

 ll _hash(char *s) {
     ll hhas = 0;
     int len = strlen(s);
     for (int i = 1; i <= len; i++)
         hhas = (hhas * hp + s[i-1]) % mod;
     return hhas;
 }
 ll getl_r(int l, int r) {
     return (hashh[r] - hashh[l-1] * _p % mod + mod) % mod;
 }

  

点双联通的新版子 就用这个(POJ那道题的)

int cas = 1;
int n, m;
int head[maxn], cnt, dfn[maxn], low[maxn];
int bcc_cnt, tim, bccno[maxn];
bool iscut[maxn];
vector<int> bcc[maxn];
stack<int> S;
struct Edge {
    int u, v, w, next;
} e[maxn*maxn];

void init() {
    bcc_cnt = cnt = 0;
    memset(head, -1, sizeof(head));
    memset(iscut, 0, sizeof(iscut));
    memset(dfn, 0, sizeof(dfn));
}

void add(int u, int v, int c) {
    e[cnt].u = u;
    e[cnt].v = v;
    e[cnt].w = c;
    e[cnt].next = head[u];
    head[u] = cnt++;
}

void tarjan(int u, int fa) {
    dfn[u] = low[u] = ++tim;
    int child = 0;
    for (int i = head[u]; ~i; i = e[i].next) {
        int v = e[i].v;
        if (!dfn[v]) {
            S.push(i);
            child++;
            tarjan(v, u);
            low[u] = min(low[u], low[v]);
            if (low[v] >= dfn[u]) {
                iscut[u] = true;
                bcc[++bcc_cnt].clear();
                while (1) {
                    int tmp = S.top(); S.pop();
                    bccno[tmp] = bcc_cnt;
                    bcc[bcc_cnt].push_back(tmp);
                    bccno[tmp^1] = bcc_cnt;
                    bcc[bcc_cnt].push_back(tmp^1);
                    if (e[tmp].u == u && e[tmp].v == v) break;
                }
            }
        }
        else if (dfn[v] < dfn[u] && v != fa) {
            S.push(i);
            low[u] = min(low[u], dfn[v]);
        }
    }
    if (fa == -1 && child == 1) iscut[u] = false;
}

void solve() {
    int cas = 1;

	while (1) {
		int n = 0;
		int u, v; scanf("%d", &u);
		if (!u) break;
		init();
		scanf("%d", &v);
		n = max(n, max(u, v));
		add(u, v, 1); add(v, u, 1);
		while (1) {
			scanf("%d", &u);
			if (!u) break;
			scanf("%d", &v);
			add(u, v, 1); add(v, u, 1);
			n = max(n, max(u, v));
		}
		printf("Network #%d\n", cas++);
		for (int i = 1; i <= n; i++)
			if (!dfn[i]) tarjan(i, -1);
		int flag = 0;
		for (int i = 1; i <= n; i++) {
			int vis[maxn] = {0}, num = 0;
			if (iscut[i]) {
				for (int j = head[i]; ~j; j = e[j].next) {
					if (!vis[bccno[j]]) {
						vis[bccno[j]] = 1;
						num++;
					}
				}
				flag |= 1;
				printf("  SPF node %d leaves %d subnets\n", i, num);
			}
		}
		if (!flag) cout << "  No SPF nodes\n";
		cout << endl;
	}
}

  

 

posted @ 2017-07-10 10:11  ost_xg  阅读(262)  评论(0)    收藏  举报