[BZOJ2887]旅行

壹、题目描述 ¶

  传送门 to DarkBZOJ

贰、题解 ¶

  我们称大地图为 \(G\),小地图为 \(g\),大地图的点 \(u\) 到小地图的映射为 \(f(u)\).

  显然,如果小地图存在欧拉回路,那么我们可以通过走大地图的每条边两次,一次走一半,将所有小地图上的边走完,并且我们在大地图上,也可以构造出一种恰好走每条边两次的方案,此时答案就是 \(\displaystyle m\times \sum_{e\in g} w_e\).

  真正的难点在于 \(g\) 只存在欧拉路的情况。在该情况下,对于一条 \(\lang u,v\rang \in G\),我们不知道如何从 \(f(u)\to f(v)\) 最大。我们假设已经走出一条路 \(P\),然后将所有 \(e\in g\land e\notin P\) 的边删掉得到 \(g'\),这样我们就成功地让我们走出来的路径 \(P\) 变成了一条欧拉路“所谓构造”,这启发我们对于走一个路径本质的分析,走出一条路,实际上就相当于删掉一些原图上的边,使得剩下的图有一条从 \(f(u)\)\(f(v)\) 的欧拉路。这个问题就变得简单许多,我们找出 \(g\) 中的两个奇点 \(x,y\),由于我们想让 \(g'\) 有一条从 \(f(u)\to f(v)\) 的欧拉路,我们需要完成三个目标:

  • \(d(f(u)),d(f(v))\) 变成奇数;
  • \(d(x),d(x)\) 变成偶数;
  • 其他点的度数不变;

  只有当我们选择几条以 \(f(u),f(v),x,y\) 为端点的路径,才可以达成上述目标,同时,我们又向让 \(g'\) 中的边权值最大,不难想到找到这几个元素任意组合的最小的最短路。因此,若从 \(f(u)\to f(v)\),最大获得价值其实就是 \(\displaystyle\sum_{e\in g}w_e-\min\{dis(f(u),f(v))+dis(x,y),dis(f(u),x)+dis(f(v),y),dis(f(u),y)+dis(f(v),x)\}\),此时我们能够解决每条大地图中的边内部小地图的最优解。

  最后一个问题,我们实际上是在 \(G\) 上面游走,那么,怎么走才能获得最优解?首先断言,我们只需要考察每条边经过了 \(1\) 次还是 \(2\) 次(\(0\) 次肯定不优),若某条边走了 \(3\) 次,就相当于分别在 \(f(u),f(v)\) 外挂了三条边,此时我们找欧拉路,实际上和只有一次(即之在外秒挂了一条边)的效果是等价的,而两次同理。因此,我们需要解决的问题就是,在一堆走了 \(1\) 次的边上,看哪些边可以变成两次,但是这样不大好,因为 \(G\) 所有边都走一次,不能保证初始方案合法,因此我们考察在所有边都走了 \(2\) 次的情况下,选择一些边,让他们只走一次,而这些边,有公共端点的构成一个个环,所以我们想在原图上找一些环,把所有的边由两次改成一次,花费最小,不难想到网络流中的循环流。不过在这个数据范围跑费用流,你的双亲真的还在吗因为一些常见的原因,我们放弃这个方法,注意到 \(m\le n+5\),因此我们可以先把 \(G\)\(dfs\) 树搞出来,用 \(\mathcal O(2^{m-n+1})\) 复杂度枚举外挂边走 \(1\) 次还是 \(2\) 次,剩下的树边可以简单差分出来。

  因此最后的复杂度就是 \(\mathcal O(p^3+2^{m-n+1}n)\).

叁、参考代码 ¶

/** @author Arextre; その可憐な少女は魔女であり、旅人でした。 ―― そう、私です。 */

#include <bits/stdc++.h>
using namespace std;

#define USING_FREAD
// #define NDEBUG
// #define NCHECK
#include <cassert>

namespace Elaina {

#define rep(i, l, r) for(int i = (l), i##_end_ = (r); i <= i##_end_; ++i)
#define drep(i, l, r) for(int i = (l), i##_end_ = (r); i >= i##_end_; --i)
#define fi first
#define se second
#define mp(a, b) make_pair(a, b)
#define Endl putchar('\n')
#define whole(v) ((v).begin()), ((v).end())
#define bitcnt(s) (__builtin_popcount(s))
#define y0 FUCK_UP
#define y1 MOTHER_FUCKER
#ifdef NCHECK
# define iputs(Content) ((void)0)
# define iprintf(Content, argvs...) ((void)0)
#else
# define iputs(Content) fprintf(stderr, Content)
# define iprintf(Content, argvs...) fprintf(stderr, Content, argvs)
#endif

typedef unsigned int uint;
typedef long long ll;
typedef unsigned long long ull;
typedef pair<int, int> pii;
typedef pair<ll, ll> pll;

template<class T> inline T fab(T x) { return x < 0 ? -x : x; }
template<class T> inline void getmin(T& x, const T rhs) { x = min(x, rhs); }
template<class T> inline void getmax(T& x, const T rhs) { x = max(x, rhs); }

#ifdef USING_FREAD
inline char qkgetc() {
# define BUFFERSIZE 1 << 18
    static char BUF[BUFFERSIZE], *p1 = BUF, *p2 = BUF;
    return p1 == p2 && (p2 = (p1 = BUF) + fread(BUF, 1, BUFFERSIZE, stdin), p1 == p2) ? EOF : *p1++;
# undef BUFFERSIZE
}
# define CHARRECEI qkgetc()
#else
# define CHARRECEI getchar()
#endif

template<class T> inline T readret(T x) {
    x = 0; int f = 0; char c;
    while(!isdigit(c = CHARRECEI)) if(c == '-') f = 1;
    for(x = (c ^ 48); isdigit(c = CHARRECEI); x = (x << 1) + (x << 3) + (c ^ 48));
    return f ? -x : x;
}
template<class T> inline void readin(T& x) {
    x = 0; int f = 0; char c;
    while((c = CHARRECEI) < '0' || '9' < c) if(c == '-') f = 1;
    for(x = (c ^ 48); isdigit(c = CHARRECEI); x = (x << 1) + (x << 3) + (c ^ 48));
    if(f) x = -x;
}
template<class T, class... Args> inline void readin(T& x, Args&... args) {
    readin(x), readin(args...);
}
// default enter
template<class T> inline void writln(T x, char s = '\n') {
    static int fwri_sta[55], fwri_ed = 0;
    if(x < 0) putchar('-'), x = -x;
    do fwri_sta[++fwri_ed] = x % 10, x /= 10; while(x);
    while(putchar(fwri_sta[fwri_ed--] ^ 48), fwri_ed);
    putchar(s);
}

} // namespace Elaina
using namespace Elaina;

const int maxn = 10005;
const int maxp = 200;

int n, m, p, q;
// big graph
vector<pii> g[maxn + 5];
int at[maxn + 5];
// small graph
int f[maxp + 5][maxp + 5];
int deg[maxp + 5], wsum;

inline void input() {
    readin(n, m, p, q);
    int u, v, w;
    rep (i, 1, n) readin(at[i]);
    rep (i, 1, m) {
        readin(u, v);
        g[u].push_back({ v, i }), g[v].push_back({ u, i });
    }
    memset(f, 0x3f, sizeof f);
    rep (i, 1, q) {
        readin(u, v, w);
        // getmin(f[u][v], w), f[v][u] = f[u][v];
        f[u][v] = f[v][u] = w;
        ++deg[u], ++deg[v], wsum += w;
    }
}

inline void floyd() {
    rep (i, 1, p) f[i][i] = 0;
    rep (k, 1, p) rep (i, 1, p) if (i ^ k)
        rep (j, 1, p) if ((j ^ i) && (j ^ k))
            getmin(f[i][j], f[i][k] + f[k][j]);
}

int x, y;
inline void getSpecialNode() {
    x = y = 0; rep (i, 1, p) if (deg[i] & 1) (!x)? x = i: y = i;
}

vector<pii> extra; bool intr[maxn * 2 + 5];
void getExtraEdge(int u, int pare) {
    static bool vis[maxn + 5]; vis[u] = true;
    for (auto e: g[u]) if (e.se ^ pare) {
        if (vis[e.fi] && u < e.fi) extra.push_back({ u, e.fi });
        else if (!vis[e.fi]) intr[e.se] = true, getExtraEdge(e.fi, e.se);
    }
}

inline int once(int u, int v) {
    u = at[u], v = at[v];
    return wsum - min(min(f[u][x] + f[v][y], f[u][y] + f[v][x]), f[u][v] + f[x][y]);
}
inline int twice(int u, int v) { return wsum - f[x][y]; }
int cnt[maxn + 5];
void dfs(int u, int fa, ll& cur) {
    for (auto e: g[u]) if ((e.fi ^ fa) && intr[e.se]) dfs(e.fi, u, cur);
    if (!fa) return; // lawks!
    if (!cnt[u]) cur += twice(u, fa);
    else cur += once(u, fa), cnt[u] ^= 1, cnt[fa] ^= 1;
}

signed main() {
    input(); floyd(); getSpecialNode();
    // Euler circuit exists.
    if (!x) return printf("%lld\n", 1ll * m * wsum) & 0;
    assert(x > 0), assert(y > 0);
    getExtraEdge(1, 0);
    ll ans = 0; int siz = (int)extra.size(), U = 1 << siz;
    for (int s = 0; s < U; ++s) {
        ll cur = 0;
        memset(cnt + 1, 0, n << 2);
        for (int i = 0; i < siz; ++i) {
            if (s >> i & 1) cur += twice(extra[i].fi, extra[i].se);
            else {
                cur += once(extra[i].fi, extra[i].se);
                cnt[extra[i].fi] ^= 1, cnt[extra[i].se] ^= 1;
            }
        }
        dfs(1, 0, cur);
        getmax(ans, cur);
    }
    writln(ans);
    return 0;
}

posted @ 2021-11-09 20:13  Arextre  阅读(31)  评论(0编辑  收藏  举报