最新文章

[ZJOI2011]道馆之战 题解

[ZJOI2011]道馆之战 题解

题目地址:洛谷:【P4679】[ZJOI2011]道馆之战 – 洛谷、BZOJ 

[JLOI2011]不重复数字 题解

[JLOI2011]不重复数字 题解

题目地址:洛谷:【P4305】[JLOI2011]不重复数字 – 洛谷、BZO 

[SDOI2011]染色 题解

[SDOI2011]染色 题解

题目地址:洛谷:【P2486】[SDOI2011]染色 – 洛谷、BZOJ:Problem 2243. — [SDOI2011]染色

题目描述

给定一棵有n个节点的无根树和m个操作,操作有2类:
1、将节点a到节点b路径上所有点都染成颜色c;
2、询问节点a到节点b路径上的颜色段数量(连续相同颜色被认为是同一段),
如“112221”由3段组成:“11”、“222”和“1”。
请你写一个程序依次完成这m个操作。

输入输出格式

输入格式:
第一行包含2个整数n和m,分别表示节点数和操作数;
第二行包含n个正整数表示n个节点的初始颜色
下面 行每行包含两个整数x和y,表示x和y之间有一条无向边。
下面 行每行描述一个操作:
“C a b c”表示这是一个染色操作,把节点a到节点b路径上所有点(包括a和b)都染成颜色c;
“Q a b”表示这是一个询问操作,询问节点a到节点b(包括a和b)路径上的颜色段数量。

输出格式:
对于每个询问操作,输出一行答案。

输入输出样例

输入样例#1:

6 5
2 2 1 2 1 1
1 2
1 3
2 4
2 5
2 6
Q 3 5
C 2 1 1
Q 3 5
C 5 1 2
Q 3 5

输出样例#1:

3
1
2

说明

数N<=10^5,操作数M<=10^5,所有的颜色C为整数且在[0, 10^9]之间。

题解

树链信息自然就是树剖。线段树维护当前区间左右端颜色以及这段区间中间有几段。合并检查拼接点是否同样颜色。树剖上跳的过程类似,实现见代码。

代码

// 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;
    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;
}

inline bool isop(char c) {
    return c == 'Q' || c == 'C';
}

inline char readop() {
    char c;
    while(!isop(c = fgc()));
    return c;
}

const int MAXN = 100005;

int n, m, w[MAXN], ut, vt, at, bt, ct;
char op;
int fa[MAXN], siz[MAXN], son[MAXN], dfn[MAXN], ptn[MAXN], top[MAXN], dep[MAXN], cnt;
std::vector<int> gra[MAXN];

inline void addedge(int u, int v) {
    gra[u].push_back(v);
    gra[v].push_back(u);
}

inline void dfs1(int u) {
    for(int i = 0; i < gra[u].size(); i++) {
        int v = gra[u][i];
        if(v == fa[u]) continue;
        dep[v] = dep[u] + 1;
        fa[v] = u;
        dfs1(v);
        siz[u] += siz[v] + 1;
        if(siz[v] > siz[son[u]]) son[u] = v;
    }
}

inline void dfs2(int u, int tp) {
    top[u] = tp;
    dfn[u] = ++cnt;
    ptn[dfn[u]] = u;
    if(son[u]) dfs2(son[u], tp);
    for(int i = 0; i < gra[u].size(); i++) {
        int v = gra[u][i];
        if(v == son[u] || v == fa[u]) continue;
        dfs2(v, v);
    }
}

struct Node {
    int l, r, tot;
    bool tag;
} sgt[MAXN << 2];

inline void pushdown(int o, int l, int r) {
    int lch = o << 1, rch = (o << 1) | 1;
    if(sgt[o].tag) {
        sgt[lch].l = sgt[lch].r = sgt[rch].l = sgt[rch].r = sgt[o].l;
        sgt[lch].tag = sgt[rch].tag = true;
        sgt[lch].tot = sgt[rch].tot = 1;
        sgt[o].tag = false;
    }
}

inline void merge(Node &rt, Node ls, Node rs) {
    rt.l = ls.l;
    rt.r = rs.r;
    rt.tot = ls.tot + rs.tot;
    if(ls.r == rs.l) rt.tot--;
}

inline void build(int o, int l, int r) {
    if(l == r) {
        sgt[o].l = sgt[o].r = w[ptn[l]];
        sgt[o].tot = 1;
        return;
    }
    int mid = (l + r) >> 1, lch = o << 1, rch = (o << 1) | 1;
    build(lch, l, mid);
    build(rch, mid + 1, r);
    merge(sgt[o], sgt[lch], sgt[rch]);
}

inline void modify(int o, int l, int r, int ll, int rr, int v) {
    if(l == ll && r == rr) {
        sgt[o].l = sgt[o].r = v;
        sgt[o].tot = 1;
        sgt[o].tag = true;
        return;
    }
    pushdown(o, l, r);
    int mid = (l + r) >> 1, lch = o << 1, rch = (o << 1) | 1;
    if(rr <= mid) {
        modify(lch, l, mid, ll, rr, v);
    } else if(ll > mid) {
        modify(rch, mid + 1, r, ll, rr, v);
    } else {
        modify(lch, l, mid, ll, mid, v);
        modify(rch, mid + 1, r, mid + 1, rr, v);
    }
    merge(sgt[o], sgt[lch], sgt[rch]);
}

inline Node query(int o, int l, int r, int ll, int rr) {
    if(l == ll && r == rr) {
        return sgt[o];
    }
    pushdown(o, l, r);
    int mid = (l + r) >> 1, lch = o << 1, rch = (o << 1) | 1;
    if(rr <= mid) {
        return query(lch, l, mid, ll, rr);
    } else if(ll > mid) {
        return query(rch, mid + 1, r, ll, rr);
    } else {
        Node ls = query(lch, l, mid, ll, mid), rs = query(rch, mid + 1, r, mid + 1, rr), res;
        merge(res, ls, rs);
        return res;
    }
}

inline void modify(int u, int v, int c) {
    int tu = top[u], tv = top[v];
    while(tu != tv) {
        if(dep[tu] > dep[tv]) {
            std::swap(tu, tv);
            std::swap(u, v);
        }
        modify(1, 1, n, dfn[tv], dfn[v], c);
        v = fa[tv];
        tv = top[v];
    }
    if(dep[u] > dep[v]) std::swap(u, v);
    modify(1, 1, n, dfn[u], dfn[v], c);
}

inline int query(int u, int v) {
    Node t;
    int res = 0, cu = -1, cv = -1, tu = top[u], tv = top[v];
    while(tu != tv) {
        if(dep[tu] > dep[tv]) {
            std::swap(tu, tv);
            std::swap(u, v);
            std::swap(cu, cv);
        }
        t = query(1, 1, n, dfn[tv], dfn[v]);
        res += t.tot;
        if(t.r == cv) res--;
        cv = t.l;
        v = fa[tv];
        tv = top[v];
    }
    if(dep[u] > dep[v]) {
        std::swap(u, v);
        std::swap(cu, cv); 
    }
    t = query(1, 1, n, dfn[u], dfn[v]);
    res += t.tot;
    if(t.l == cu) res--;
    if(t.r == cv) res--;
    return res;
}

int main() {
    n = readint(); m = readint();
    for(int i = 1; i <= n; i++) w[i] = readint();
    for(int i = 1; i < n; i++) {
        ut = readint(); vt = readint();
        addedge(ut, vt);
    }
    dfs1(1);
    dfs2(1, 1);
    build(1, 1, n);
    while(m--) {
        op = readop();
        at = readint();
        bt = readint();
        if(op == 'Q') {
            printf("%d\n", query(at, bt));
        }
        if(op == 'C') {
            ct = readint();
            modify(at, bt, ct);
        }
    }
    return 0;
}
[HAOI2015]树上操作 题解

[HAOI2015]树上操作 题解

题目地址:洛谷:【P3178】[HAOI2015]树上操作 – 洛谷、BZOJ 

树链剖分(轻重链剖分)原理与实现

树链剖分(轻重链剖分)原理与实现

概述 树链剖分是一种将树上的链划分为轻重链,从而实现降低复杂度的处理方式。剖分后的树,单次 

[HDU5385]The path 题解

[HDU5385]The path 题解

题目地址:HDUOJ:Problem – 5385

题目描述

You have a connected directed graph.Let d(x) be the length of the shortest path from 1 to x.Specially d(1)=0.A graph is good if there exist x satisfy d(1)<d(2)<….d(x)>d(x+1)>…d(n).Now you need to set the length of every edge satisfy that the graph is good.Specially,if d(1)<d(2)<..d(n),the graph is good too.
The length of one edge must ∈ [1,n]
It’s guaranteed that there exists solution.
给你一个有向图,你要确定每条边的边权,使得存在1到每个点的最短路距离d满足d(1)<d(2)<….d(x)>d(x+1)>…d(n)。

输入输出格式

输入格式:
There are multiple test cases. The first line of input contains an integer T, indicating the number of test cases. For each test case:
The first line contains two integers n and m,the number of vertexs and the number of edges.Next m lines contain two integers each, ui and vi (1≤ui,vi≤n), indicating there is a link between nodes ui and vi and the direction is from ui to vi.
∑n≤3∗10^5,∑m≤6∗10^5
1≤n,m≤10^5

输出格式:
For each test case,print m lines.The i-th line includes one integer:the length of edge from ui to vi

输入输出样例

输入样例#1:

2
4 6
1 2
2 4
1 3
1 2
2 2
2 3
4 6
1 2
2 3
1 4
2 1
2 1
2 1

输出样例#1:

1
2
2
1
4
4
1
1
3
4
4
4

题解

其实这道题如果能想到Prim算法的流程就好做了。Prim是确定一个点的d为0,然后向外扩展到当前选定点集距离最小的点,扩展完毕以后就是一棵最短路树/最小生成树。那么我们也来构造这样一棵最短路树/最小生成树,使得树边满足题目要求且非树边边权极大就可以了。
我们确定1为树根,d(1)=0,然后从最小号点和最大号点开始往里加点,每加一个点就给它的出边和相邻点打标记,表示这些边/点以后应当计算。
虽然数据范围长得很像O(n \log n),但其实算法是O(m)的。

代码

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

#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;
    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 = 600005;

struct Edge {
    int u, v, id;
};

int T, n, m, ut, vt, dis[MAXN];
std::vector<Edge> gra[MAXN], edges;
bool vis[MAXN], ans[MAXN];

inline void work(int u, int d) {
    dis[u] = d;
    for(int i = 0; i < gra[u].size(); i++) {
        int v = gra[u][i].v;
        if(vis[v]) continue;
        vis[v] = true;
        ans[gra[u][i].id] = true;
    }
}

int main() {
    T = readint();
    while(T--) {
        n = readint(); m = readint();
        memset(dis, 0, sizeof(dis));
        memset(ans, 0, sizeof(ans));
        memset(vis, 0, sizeof(vis));
        for(int i = 1; i <= n; i++) gra[i].clear();
        edges.clear();
        for(int i = 1; i <= m; i++) {
            ut = readint(); vt = readint();
            gra[ut].push_back(Edge {ut, vt, i});
            edges.push_back(Edge {ut, vt, i});
        }
        int l = 1, r = n, d = 0;
        vis[l] = true;
        while(l <= r) {
            if(vis[l]) work(l++, d++);
            if(vis[r]) work(r--, d++);
        }
        for(int i = 1; i <= m; i++) {
            if(ans[i]) {
                printf("%d\n", std::abs(dis[edges[i - 1].u] - dis[edges[i - 1].v]));
            } else {
                printf("%d\n", n);
            }
        }
    }
    return 0;
}
[POI2001]Peaceful Commission 题解 & 2-SAT题目解法

[POI2001]Peaceful Commission 题解 & 2-SAT题目解法

题目地址:HDUOJ:Problem – 1814 题目描述 The Publ 

[APIO2013]机器人 题解

[APIO2013]机器人 题解

题目地址:洛谷:【P3638】[APIO2013]机器人 – 洛谷、BZOJ:Problem 3205. — [Apio2013]机器人

题目描述

VRI(Voltron 机器人学会)的工程师建造了 n 个机器人。任意两个兼容的机 器人站在同一个格子时可以合并为一个复合机器人。
我们把机器人用 1 至 n 编号(n ≤ 9)。如果两个机器人的编号是连续的,那 么它们是兼容的,可以合并成一个复合机器人。最初这 n 个机器人各自都只有唯 一的编号。而一个由两个或以上的机器人合并构成的复合机器人拥有两个编号, 分别是构成它的所有机器人中最小和最大的编号。
例如,2 号机器人只可以与 1 号或 3 号机器人合并。若 2 号机器人与 3 号机 器人合并,可构成编号为 2-3 的复合机器人。如果编号为 2-3 的复合机器人与编 号为 4-6 的复合机器人合并,可构成编号为 2-6 的复合机器人。当所有机器人合 并以后则构成 1-n 复合机器人。
工程师把这 n 个机器人放在了一个封闭的房间中,房间四周均是墙。该房间 被划分成 w × h 个方格。有些方格有障碍物,机器人不可经过或停留;其余方格 允许多个机器人停留,同时允许机器人经过。任何时候一个机器人只占用一个方 格。初始时刻,所有机器人均在不同的方格中。
这些原始的机器人不会自发地移动。它们只有被工程师沿 x 轴或 y 轴推动 后,才会沿推动的方向不断向前直线移动,直至碰到障碍物或墙停止移动。停止 移动后,它会扫描当前的格子是否存在可以与它合并的机器人,如果有,则合并 并继续检查,直至不能再合并为止。工程师只能沿水平向左、水平向右、竖直向 上、竖直向下四个方向推动机器人,并且,在机器人尚未停止移动时,不允许推 动其它机器人,因此任何时刻,房间中都只能有一个机器人移动。
为了帮助机器人转向,工程师在一些格子中放置了转向器。具体地说,转向 器分为顺时针转向器(右转器)和逆时针转向器(左转器),顺时针转向器可以 使到达该格子的机器人沿顺时针方向转向 90°;逆时针转向器可以使到达该格 子的机器人沿逆时针方向转向 90°。
现在,我们将告诉你初始时刻房间内的信息。请你计算工程师最少共计需要 推动机器人多少次,才能把所有的 n 个机器人全部合并(如果可能的话)。

输入输出格式

输入格式:
你的程序必须从标准输入读入。
输入的第 1 行包含 3 个整数 n、w 和 h,用 空格隔开。
输入文件中接下来的 h 行描述初始时刻房间内的信息,每行包含 w 个字符。 这 w × h 个字符中每一个表示房间中的一个格子,意义如下:
‘1’至‘9’:表示该方格中有一个机器人,编号为这个数字;
‘x’:表示该方格有障碍物;
‘A’:表示该方格中有一个逆时针转向器;
‘C’:表示该方格中有一个顺时针转向器;
‘.’:表示该方格为空地。

输出格式:
你的程序必须输出到标准输出。输出仅一个整数,表示最少需要推动的次数。 若不能使所有机器人全部合并,输出-1。

输入输出样例

输入样例#1:

4 10 5 
1.........
AA...x4...
..A..x....
2....x....
..C.3.A...

输出样例#1:

5

说明

第一步:向右推动 3 号机器人,当它碰到转向器后会向上继续移动,直至碰 到墙壁停止移动。
第二步:向上推动 4 号机器人,当它碰到墙壁后停止移动,与 3 号机器人合 并,构成 3-4 号机器人
第三步:向上推动 2 号机器人,当它碰到转向器后会向左移动,由于左侧为 墙壁,故停留在原地。
第四步:向右推动 2 号机器人,由于它在一个转向器上,故它会向上移动, 直至碰到墙壁停止移动,与 1 号机器人合并,构成 1-2 号机器人。
第五步:向左推动 3-4 号机器人,当它碰到墙壁后停止移动,与 1-2 号机器 人合并,构成 1-4 号机器人。
我们将使用以下 4 类输入测例测试你的程序。

  1. (10 分)测例满足 n = 2,w ≤ 10 且 h ≤ 10,没有任何转向器。
  2. (20 分)测例满足 n = 2,w ≤ 10 且 h ≤ 10。
  3. (30 分)测例满足 n ≤ 9,w ≤ 300 且 h ≤ 300。
  4. (40 分)测例满足 n ≤ 9,w ≤ 500 且 h ≤ 500。

题解

首先这是一个斯坦纳树的模型,但是比较棘手的是细节部分。
首先在斯坦纳树问题中的边在这里变成了从一个点向某个方向走最终能到达哪里这样的一个问题,就需要用记忆化搜索预处理出这个。而这个DFS细节又比较多,比较难写。
然后我们设计DP状态dp[l][r][i][j]表示在点(i, j)将编号为[l, r]的机器人组合在一起所需的最少推动次数。斯坦纳树问题的第一种转移在这里变成了一个区间DP的套路,方程如下
dp[l][r][i][j] = \min_{k \in [l, r]} \{ dp[l][k][i][j] + dp[k + 1][r][i][j] \}
而第二种转移仍然是从这个点到向某个方向推的下一个点
dp[l][r][i][j] = \min \{ dp[l][r][i'][j'] + 1 \}
第二种转移依旧要依赖于SPFA来做,可是SPFA的复杂度不靠谱,我们考虑优化成下面的形式。
我们把SPFA的出发点集合按dp值先排个序。由于dp值不会很大,可以采用计数排序,实现O(n)的排序。把排序后的点扔进队列Q1,然后把扩展出来的点扔进队列Q2。每次要松弛的时候从两个队列的队首拿dp值最小的来松弛。这种优化方法只有当图的边权全部为1的时候才能用。
答案就是\min \{ dp[1][n][i][j] \}
吐槽:洛谷上这个题卡常的厉害,我优化成底下这个代码的样子还没过,最后开了O2苟过去的。

代码

题解代码参考并借鉴了[APIO2013] – FallDream – 博客园中的写法,感谢原作者。

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

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;
}

inline bool ismap(register char c) {
    return c == 'A' || c == 'C' || c == '.' || c == 'x' || (c > '0' && c <= '9');
}

inline char readmap() {
    register char c;
    while(!ismap(c = fgc()));
    return c;
}

const int MAXK = 10, MAXN = 505, INF = 0x3f3f3f3f;
const int fix[2][4] = {{-1, 0, 1, 0}, {0, 1, 0, -1}};

int k, n, m, clk, top; 
int dp[MAXK][MAXK][MAXN][MAXN], vis[MAXN][MAXN][4], v[200005], rk[200005];
char mmp[MAXN][MAXN];
bool inque[MAXN][MAXN];

struct Point {
    int x, y;
} pts[MAXK], nxt[MAXN][MAXN][4], q[250005];

struct Queue {
    Point q[250005];
    static const int len = 250000;
    int l, r;
    Queue() {
        l = r = 0;
    }
    inline void clear() {
        l = r = 0;
    }
    inline void push(Point p) {
        q[r++] = p;
        if(r > len) r -= len;
    }
    inline Point front() {
        return q[l];
    }
    inline void pop() {
        l++;
        if(l > len) l -= len;
    }
    inline bool empty() {
        return l == r;
    }
}; 

Queue q1, q2;

inline Point dfs(int x, int y, int dir) {
    if(vis[x][y][dir] == clk) {
        nxt[x][y][dir].x = nxt[x][y][dir].y = -1; 
        return nxt[x][y][dir];
    }
    vis[x][y][dir] = clk;
    if(nxt[x][y][dir].x && nxt[x][y][dir].y) return nxt[x][y][dir];
    register int ndir = dir;
    if(mmp[x][y] == 'A') ndir = dir + 3;
    else if(mmp[x][y] == 'C') ndir = dir + 1;
    if(ndir >= 4) ndir -= 4;
    register int nx = x + fix[0][ndir], ny = y + fix[1][ndir];
    if(nx < 1 || nx > n || ny < 1 || ny > m || mmp[nx][ny] == 'x') {
        nxt[x][y][dir].x = x;
        nxt[x][y][dir].y = y;
        return nxt[x][y][dir];
    }
    return nxt[x][y][dir] = dfs(nx, ny, ndir);
}

inline void spfa(register int l, register int r) {
    // 计数排序
    memset(v, 0, sizeof(v));
    register int mx = 0;
    for(register int i = 1; i <= top; i++) {
        if(mx < dp[l][r][q[i].x][q[i].y]) mx = dp[l][r][q[i].x][q[i].y];
        v[dp[l][r][q[i].x][q[i].y]]++;
    }
    for(register int i = 1; i <= mx; i++) v[i] += v[i - 1];
    for(register int i = 1; i <= top; i++) rk[v[dp[l][r][q[i].x][q[i].y]]--] = i;
    for(register int i = 1; i <= top; i++) q1.push(q[rk[i]]);
    top = 0;
    // SPFA
    while(!q1.empty() || !q2.empty()) {
        Point t;
        if(q1.empty() || (!q2.empty() && dp[l][r][q1.front().x][q1.front().y] > dp[l][r][q2.front().x][q2.front().y])) {
            t = q2.front(); q2.pop();
        } else {
            t = q1.front(); q1.pop();
        }
        inque[t.x][t.y] = false;
        for(register int i = 0; i < 4; i++) {
            Point nx = nxt[t.x][t.y][i];
            if(nx.x == -1 || nx.y == -1) continue;
            if(dp[l][r][nx.x][nx.y] > dp[l][r][t.x][t.y] + 1) {
                dp[l][r][nx.x][nx.y] = dp[l][r][t.x][t.y] + 1;
                if(!inque[nx.x][nx.y]) {
                    inque[nx.x][nx.y] = true;
                    q2.push(nx);
                }
            }
        }
    }
}

int main() {
    k = readint(); m = readint(); n = readint();
    for(register int i = 1; i <= n; i++) {
        for(register int j = 1; j <= m; j++) {
            mmp[i][j] = readmap();
            if(mmp[i][j] > '0' && mmp[i][j] <= '9') {
                int t = mmp[i][j] - '0';
                pts[t].x = i;
                pts[t].y = j;
            }
        }
    }
    for(register int i = 1; i <= n; i++) {
        for(register int j = 1; j <= m; j++) {
            if(mmp[i][j] != 'x') {
                for(register int k = 0; k < 4; k++) {
                    clk++;
                    dfs(i, j, k);
                }
            }
        }
    }
    memset(dp, 0x3f, sizeof(dp));
    for(register int i = 1; i <= k; i++) {
        inque[pts[i].x][pts[i].y] = true;
        q[++top] = pts[i];
        dp[i][i][pts[i].x][pts[i].y] = 0;
        spfa(i, i);
    }
    for(register int len = 2; len <= k; len++) {
        for(register int l = 1; l + len - 1 <= k; l++) {
            register int r = l + len - 1;
            for(register int i = 1; i <= n; i++) {
                for(register int j = 1; j <= m; j++) {
                    for(register int k = l; k < r; k++) {
                        if(dp[l][r][i][j] > dp[l][k][i][j] + dp[k + 1][r][i][j]) {
                            dp[l][r][i][j] = dp[l][k][i][j] + dp[k + 1][r][i][j];
                        }
                    }
                    if(dp[l][r][i][j] < INF) {
                        q[++top].x = i;
                        q[top].y = j;
                        inque[i][j] = true;
                    }
                }
            }
            spfa(l, r);
        }
    }
    register int ans = INF;
    for(register int i = 1; i <= n; i++) {
        for(register int j = 1; j <= m; j++) {
            if(ans > dp[1][k][i][j]) ans = dp[1][k][i][j];
        }
    }
    if(ans == INF) {
        puts("-1");
    } else {
        printf("%d", ans);
    }
    return 0;
}
[WC2008]游览计划 题解 & 斯坦纳树类题目解法

[WC2008]游览计划 题解 & 斯坦纳树类题目解法

题目地址:洛谷:【P4294】[WC2008]游览计划 – 洛谷、BZOJ:P