标签: 图论

[SDOI2015]星际战争 题解

[SDOI2015]星际战争 题解

题目地址:洛谷:【P3324】[SDOI2015]星际战争 – 洛谷、BZOJ 

[USACO15DEC]最大流Max Flow 题解

[USACO15DEC]最大流Max Flow 题解

题目地址:洛谷:【P3128】[USACO15DEC]最大流Max Flow &#8211 

[SCOI2007]修车 题解

[SCOI2007]修车 题解

题目地址:洛谷:【P2053】[SCOI2007]修车 – 洛谷、BZOJ:Problem 1070. — [SCOI2007]修车

题目描述

同一时刻有N位车主带着他们的爱车来到了汽车维修中心。维修中心共有M位技术人员,不同的技术人员对不同的车进行维修所用的时间是不同的。现在需要安排这M位技术人员所维修的车及顺序,使得顾客平均等待的时间最小。
说明:顾客的等待时间是指从他把车送至维修中心到维修完毕所用的时间。

输入输出格式

输入格式:
第一行有两个数M,N,表示技术人员数与顾客数。
接下来n行,每行m个整数。第i+1行第j个数表示第j位技术人员维修第i辆车需要用的时间T。

输出格式:
最小平均等待时间,答案精确到小数点后2位。

输入输出样例

输入样例#1:

2 2
3 2
1 4

输出样例#1:

1.50

说明

(2<=M<=9,1<=N<=60), (1<=T<=1000)

题解

考虑使用最小费用最大流。我们需要考虑1.车的顺序2.修车的人,那么考虑把两个状态放在一起拆点,即让某个点代表顺序第几位修车人是谁的含义。这样,实际上我们就是在求二分图的最小权匹配了。权值的计算要考虑到这个位置对答案的贡献,显然倒数第i个修车的时间会对i个人的等待时间产生贡献,因此不妨把状态转成倒数第几位修车人是谁,这样权值就变成了产生贡献的人数*修车时间。
注意这里这些工人是可以同时开工的,也就是说,可以把车对应到同一时间的不同工人上。

代码

// Code by KSkun, 2018/4
#include <cstdio>
#include <cstring>

#include <vector>
#include <queue>

typedef long long LL;

inline char fgc() {
    static char buf[100000], *p1 = buf, *p2 = buf;
    return p1 == p2 && (p2 = (p1 = buf) + fread(buf, 1, 100000, stdin), p1 == p2) ? EOF : *p1++;
}

inline int readint() {
    register int res = 0, neg = 1;
    register char c = fgc();
    while(c < '0' || c > '9') {
        if(c == '-') neg = -1;
        c = fgc();
    }
    while(c >= '0' && c <= '9') {
        res = res * 10 + c - '0';
        c = fgc();
    }
    return res * neg;
}

const int MAXN = 100005, INF = 1e9;

struct Edge {
    int to, cap, cost, nxt;
} gra[MAXN << 1];
int head[MAXN], tot;

inline void addedge(int u, int v, int cap, int cost) {
    gra[tot] = Edge {v, cap, cost, head[u]}; head[u] = tot++;
    gra[tot] = Edge {u, 0, -cost, head[v]}; head[v] = tot++;
}

int flow, cost, pre[MAXN], pree[MAXN], f[MAXN], dis[MAXN];
bool inque[MAXN];
std::queue<int> que;

inline void mcmf(int s, int t) {
    for(;;) {
        memset(f, 0, sizeof(f));
        memset(dis, 0x3f, sizeof(dis));
        dis[s] = 0; f[s] = INF; inque[s] = true; que.push(s);
        while(!que.empty()) {
            int u = que.front(); que.pop(); inque[u] = false;
            for(int i = head[u]; ~i; i = gra[i].nxt) {
                int v = gra[i].to;
                if(gra[i].cap > 0 && dis[v] > dis[u] + gra[i].cost) {
                    pre[v] = u; pree[v] = i;
                    f[v] = std::min(f[u], gra[i].cap);
                    dis[v] = dis[u] + gra[i].cost;
                    if(!inque[v]) {
                        inque[v] = true; que.push(v);
                    }
                }
            }
        }
        if(f[t] == 0) break;
        for(int i = t; i != s; i = pre[i]) {
            gra[pree[i]].cap -= f[t]; gra[pree[i] ^ 1].cap += f[t];
        }
        flow += f[t]; cost += f[t] * dis[t];
    }
}

int n, m, t[70][70], S, T;

// 1~n car
// n+1~mn+n staff
// mn+n+1 s mn+n+2 t
int main() {
    memset(head, -1, sizeof(head));
    m = readint(); n = readint();
    for(int i = 1; i <= n; i++) {
        for(int j = 1; j <= m; j++) {
            t[i][j] = readint();
        }
    }
    S = m * n + n + 1; T = S + 1;
    for(int i = 1; i <= n; i++) {
        addedge(S, m * n + i, 1, 0);
    }
    for(int i = 1; i <= m; i++) { // which worker
        for(int j = 1; j <= n; j++) { // which time
            int p = (i - 1) * n + j;
            for(int k = 1; k <= n; k++) { // which car
                addedge(m * n + k, p, 1, j * t[k][i]);
            }
            addedge(p, T, 1, 0);
        }
    }
    mcmf(S, T);
    printf("%.2lf", cost / double(n));
    return 0;
}
[NOI2014]魔法森林 题解

[NOI2014]魔法森林 题解

题目地址:洛谷:【P2387】[NOI2014]魔法森林 – 洛谷、BZOJ: 

[ZJOI2015]地震后的幻想乡 题解

[ZJOI2015]地震后的幻想乡 题解

题目地址:洛谷:【P3343】[ZJOI2015]地震后的幻想乡 – 洛谷、B 

[SPOJ-QTREE5]Query on a tree V 题解

[SPOJ-QTREE5]Query on a tree V 题解

题目地址:洛谷:【SP2939】QTREE5 – Query on a tree V – 洛谷、SPOJ:SPOJ.com – Problem QTREE5

SPOJ QTREE系列:

题目描述

You are given a tree (an acyclic undirected connected graph) with N nodes. The tree nodes are numbered from 1 to N. We define dist(a, b) as the number of edges on the path from node a to node b.
Each node has a color, white or black. All the nodes are black initially.
We will ask you to perfrom some instructions of the following form:

  • 0 i : change the color of i-th node(from black to white, or from white to black).
  • 1 v : ask for the minimum dist(u, v), node u must be white(u can be equal to v). Obviously, as long as node v is white, the result will always be 0.

给一棵树,最开始点都是黑色,操作1.改变点的颜色2.求离点v最近的白点距离。

输入输出格式

输入格式:
In the first line there is an integer N (N <= 100000)
In the next N-1 lines, the i-th line describes the i-th edge: a line with two integers a b denotes an edge between a and b.
In the next line, there is an integer Q denotes the number of instructions (Q <= 100000)
In the next Q lines, each line contains an instruction “0 i” or “1 v”

输出格式:
For each “1 v” operation, print one integer representing its result. If there is no white node in the tree, you should write “-1”.

输入输出样例

输入样例#1:

10
1 2
1 3
2 4
1 5
1 6
4 7
7 8
5 9
1 10
10
0 6
0 6
0 6
1 3
0 1
0 1
1 3
1 10
1 4
1 6

输出样例#1:

2
2
2
3
0

题解

本题可以从QTREE4的边分写法改过来。
本题维护堆的方法与QTREE4类似。查询答案时,从该点对应的中心边从底向上查,离该点最近的白点肯定与其在同一子树中或者在被某一中心边分开的两个子树中,分别更新答案即可。由于我们将该点对应的所有中心边都找了一遍,因此不会存在在同一子树中却从更远的中心边绕了弯的情况,因为在更近的中心边已经更新了答案了。

代码

// Code by KSkun, 2018/3
#include <cstdio>
#include <cstring>

#include <vector>
#include <algorithm>
#include <queue>

typedef long long LL;

inline char fgc() {
    static char buf[100000], *p1 = buf, *p2 = buf;
    return p1 == p2 && (p2 = (p1 = buf) + fread(buf, 1, 100000, stdin), p1 == p2) ? EOF : *p1++;
}

inline LL readint() {
    register LL res = 0, neg = 1;
    register char c = fgc();
    while(c < '0' || c > '9') {
        if(c == '-') neg = -1;
        c = fgc();
    }
    while(c >= '0' && c <= '9') {
        res = res * 10 + c - '0';
        c = fgc();
    }
    return res * neg;
}

const int MAXN = 200005, INF = 2e9;

int n, q, col[MAXN], ans;

struct Edge {
    int to, w, nxt;
} gra[MAXN << 1], grao[MAXN << 1];
int head[MAXN], heado[MAXN], ecnt, ecnto;

inline void addedge(int u, int v, int w) {
    gra[ecnt] = Edge {v, w, head[u]}; head[u] = ecnt++;
}

inline void addedgeo(int u, int v, int w) {
    grao[ecnto] = Edge {v, w, heado[u]}; heado[u] = ecnto++;
}

inline void rebuild(int u, int fa) {
    int ff = 0;
    for(int i = heado[u]; ~i; i = grao[i].nxt) {
        int v = grao[i].to, w = grao[i].w;
        if(v == fa) continue;
        if(!ff) {
            addedge(u, v, w);
            addedge(v, u, w);
            ff = u;
        } else {
            int k = ++n;
            addedge(ff, k, 0);
            addedge(k, ff, 0);
            addedge(k, v, w);
            addedge(v, k, w);
            ff = k;
        }
        rebuild(v, u);
    }
}

bool del[MAXN << 1];
int ct, ctsiz, sum;
int siz[MAXN], msz[MAXN];

inline void calsiz(int u, int fa) {
    siz[u] = 1;
    for(int i = head[u]; ~i; i = gra[i].nxt) {
        int v = gra[i].to;
        if(del[i >> 1] || v == fa) continue;
        calsiz(v, u);
        siz[u] += siz[v];
    }
}

inline void findct(int u, int fa) {
    for(int i = head[u]; ~i; i = gra[i].nxt) {
        int v = gra[i].to;
        if(del[i >> 1] || v == fa) continue;
        findct(v, u);
        int vsiz = std::max(siz[v], sum - siz[v]);
        if(vsiz < ctsiz) {
            ct = i;
            ctsiz = vsiz;
        }
    }
}

struct DisData {
    int u, d;
    inline bool operator<(const DisData &rhs) const {
        return d > rhs.d;
    }
};

std::priority_queue<DisData> s[MAXN][2];
int cnt;

struct NodeData {
    int bel, side, dis;
};
std::vector<NodeData> ndata[MAXN];

inline void caldis(int u, int fa, int d, int t, int l) {
    ndata[u].push_back(NodeData {t, l, d});
    for(int i = head[u]; ~i; i = gra[i].nxt) {
        int v = gra[i].to, w = gra[i].w;
        if(del[i >> 1] || v == fa) continue;
        caldis(v, u, d + w, t, l);
    }
}

int lch[MAXN], rch[MAXN], ctw[MAXN];

inline void update(int p) {
    while(!s[p][0].empty() && !col[s[p][0].top().u]) s[p][0].pop();
    while(!s[p][1].empty() && !col[s[p][1].top().u]) s[p][1].pop();
}

inline int divide(int u) {
    calsiz(u, 0);
    ct = -1; ctsiz = INF; sum = siz[u]; findct(u, 0);
    if(ct == -1) return 0;
    int x = gra[ct].to, y = gra[ct ^ 1].to;
    del[ct >> 1] = true;
    int t = ++cnt; 
    ctw[t] = gra[ct].w;
    caldis(x, 0, 0, t, 0); caldis(y, 0, 0, t, 1);
    lch[t] = divide(x); rch[t] = divide(y);
    return t;
}

inline void setwhite(int u) {
    for(int i = ndata[u].size() - 1; i >= 0; i--) {
        NodeData d = ndata[u][i];
        s[d.bel][d.side].push(DisData {u, d.dis});
        update(d.bel);
    }
}

inline void setblack(int u) {
    for(int i = ndata[u].size() - 1; i >= 0; i--) {
        NodeData d = ndata[u][i];
        update(d.bel);
    }
}

inline int query(int u) {
    int mn = INF;
    for(int i = ndata[u].size() - 1; i >= 0; i--) {
        NodeData d = ndata[u][i];
        if(!s[d.bel][d.side].empty()) mn = std::min(mn, s[d.bel][d.side].top().d + d.dis);
        if(!s[d.bel][d.side ^ 1].empty()) mn = std::min(mn, s[d.bel][d.side ^ 1].top().d + d.dis + ctw[d.bel]);
    }
    return mn;
}

int op, ut, vt;

int main() {
    memset(head, -1, sizeof(head));
    memset(heado, -1, sizeof(heado));
    n = readint();
    int white = 0;
    for(int i = 1; i < n; i++) {
        ut = readint(); vt = readint();
        addedgeo(ut, vt, 1);
        addedgeo(vt, ut, 1);
    }
    rebuild(1, 0);
    divide(1);
    q = readint();
    while(q--) {
        op = readint();
        ut = readint();
        if(op == 1) {
            if(!white) {
                puts("-1");
            } else if(col[ut]) {
                puts("0");
            } else {
                printf("%d\n", query(ut));
            }
        } else {
            col[ut] ^= 1;
            if(!col[ut]) {
                setblack(ut);
                white--;
            } else {
                setwhite(ut);
                white++;
            }
        }
    }
    return 0;
}
[SPOJ-QTREE4]Query on a tree IV 题解

[SPOJ-QTREE4]Query on a tree IV 题解

题目地址:洛谷:【SP2666】QTREE4 – Query on a tre 

边分治原理及实现

边分治原理及实现

树分治系列: 点分治原理与实现 | KSkun’s Blog 边分治原理及实现 

[SPOJ-PT07J]Query on a tree III 题解

[SPOJ-PT07J]Query on a tree III 题解

题目地址:洛谷:【SP1487】PT07J – Query on a tree III – 洛谷、SPOJ:SPOJ.com – Problem PT07J

SPOJ QTREE系列:

题目描述

You are given a node-labeled rooted tree with n nodes.
Define the query (x, k): Find the node whose label is k-th largest in the subtree of the node x. Assume no two nodes have the same labels.
给一棵带点权的以1为根的有根树,查询某子树内点权第k小值。

输入输出格式

输入格式:
The first line contains one integer n (1 <= n <= 105). The next line contains n integers li (0 <= li <= 109) which denotes the label of the i-th node.
Each line of the following n – 1 lines contains two integers u, v. They denote there is an edge between node u and node v. Node 1 is the root of the tree.
The next line contains one integer m (1 <= m <= 104) which denotes the number of the queries. Each line of the next m contains two integers x, k. (k <= the total node number in the subtree of x)

输出格式:
For each query (x, k), output the index of the node whose label is the k-th largest in the subtree of the node x.

输入输出样例

输入样例#1:

5
1 3 5 2 7
1 2
2 3
1 4
3 5
4
2 3
4 1
3 2
3 2

输出样例#1:

5
4
5
5

题解

DFS序+主席树。主席树的叶子节点可以存一下DFS序号,这样方便查。

代码

// Code by KSkun, 2018/3
#include <cstdio>

#include <vector>
#include <algorithm>

typedef long long LL;

inline char fgc() {
    static char buf[100000], *p1 = buf, *p2 = buf;
    return p1 == p2 && (p2 = (p1 = buf) + fread(buf, 1, 100000, stdin), p1 == p2) ? EOF 
        : *p1++;
}

inline LL readint() {
    register LL res = 0, neg = 1;
    register char c = fgc();
    while(c < '0' || c > '9') {
        if(c == '-') neg = -1;
        c = fgc();
    }
    while(c >= '0' && c <= '9') {
        res = res * 10 + c - '0';
        c = fgc();
    }
    return res * neg;
}

const int MAXN = 100005;

std::vector<int> gra[MAXN];

int dfn[MAXN], ptn[MAXN], siz[MAXN], clk;

inline void dfs(int u, int fa) {
    dfn[u] = ++clk;
    ptn[dfn[u]] = u;
    siz[u] = 1;
    for(int v : gra[u]) {
        if(v == fa) continue;
        dfs(v, u);
        siz[u] += siz[v];
    }
}

struct SGT {
    struct SGTNode {
        int lch, rch, val, dfn;
    } tr[MAXN * 20];
    int rt[MAXN], cnt = 0;

    inline void insert(int &o, int l, int r, int x, int dfn) {
        tr[++cnt] = tr[o]; o = cnt;
        tr[o].val++;
        if(l == r) {
            tr[o].dfn = dfn;
            return;
        }
        int mid = (l + r) >> 1;
        if(x <= mid) insert(tr[o].lch, l, mid, x, dfn);
        else insert(tr[o].rch, mid + 1, r, x, dfn);
    }

    inline int query(int o1, int o2, int l, int r, int k) {
        if(l == r) return ptn[tr[o2].dfn];
        int mid = (l + r) >> 1;
        if(k <= tr[tr[o2].lch].val - tr[tr[o1].lch].val) {
            return query(tr[o1].lch, tr[o2].lch, l, mid, k);
        } else {
            k -= tr[tr[o2].lch].val - tr[tr[o1].lch].val;
            return query(tr[o1].rch, tr[o2].rch, mid + 1, r, k);
        }
    }
} sgt;

int n, m, w[MAXN], ut, vt, xt, kt;
std::vector<int> tmp;

int main() {
    n = readint();
    tmp.push_back(-1);
    for(int i = 1; i <= n; i++) {
        w[i] = readint();
        tmp.push_back(w[i]);
    }
    std::sort(tmp.begin(), tmp.end());
    tmp.erase(std::unique(tmp.begin(), tmp.end()), tmp.end());
    int N = tmp.size() - 1;
    for(int i = 1; i <= n; i++) {
        w[i] = std::lower_bound(tmp.begin(), tmp.end(), w[i]) - tmp.begin();
    }
    for(int i = 1; i < n; i++) {
        ut = readint(); vt = readint();
        gra[ut].push_back(vt);
        gra[vt].push_back(ut);
    }
    dfs(1, 0);
    for(int i = 1; i <= n; i++) {
        sgt.rt[i] = sgt.rt[i - 1];
        sgt.insert(sgt.rt[i], 1, N, w[ptn[i]], i);
    }
    m = readint();
    while(m--) {
        xt = readint(); kt = readint();
        printf("%d\n", sgt.query(sgt.rt[dfn[xt] - 1], sgt.rt[dfn[xt] + siz[xt] - 1], 
            1, N, kt));
    }
    return 0;
}
[SPOJ-QTREE3]Query on a tree again! 题解

[SPOJ-QTREE3]Query on a tree again! 题解

题目地址:SPOJ:SPOJ.com – Problem QTREE3 SPO