洛谷 P4510 - [CTSC2015]性别改造计划(网络流+状压 dp)

洛谷题面传送门

一道很烦的网络流题,代码每一块的结构都很清晰,但是拼起来就让人觉得很烦……

首先此题的一大难点在于读题,我一开始傻傻地以为散点没有用,心想这题不是 sb 状压 dp 吗,后来才发现是自己都错题了……事实上本题正确的题意如下:

  • 对于血缘链上的点,任何一条链上的第 \(j\) 个点的深度都是 \(j\)
  • 对于不在血缘链上的点,我们可以为其附上适当的深度使得对于所有“繁衍关系”,都有 \(dep_x=dep_y\)

如果没有 \(\lfloor 10\ln(1+A)\rfloor\) 这个系数的存在,那么不难发现此题就是一个裸的集合划分模型。具体来说建立源汇,定义一个点与源点相连当且仅当它的性别没变,与汇点相连当且仅当它的性别变了,然后用一组方案的割的费用来刻画一个方案对应的收益,首先对每个点连边 \(S\to i\),容量 \(c_i\)。对于每组繁衍关系我们新建两个点 \(P_1,P_2\) 分别表示同时变和同时不变的收益:

  • 对于 \(P_1\),连边 \(S\to P_1\),容量 \(b_i\),再连边 \(P_1\to x,P_1\to y\),容量均 \(\infty\),此时如果 \(P_1\)\(S\) 相连但 \(x,y\) 之一与 \(T\) 相连显然割的权值就无穷大了。
  • 对于 \(P_2\) 也同理,连边 \(P_2\to T\),容量 \(\lfloor b_id_i\rfloor\),再连边 \(x\to P_2,y\to P_2\),容量均 \(\infty\)

然后拿 \(\sum b_i+\lfloor b_id_i\rfloor\) 减去最小割就是答案。

(事实上真实情况是:如果没有系数那么直接每个点都保持原来的性别即可)

现在有了这个系数的存在。乍一看以为需要奇怪的降维技巧,但实际上发现 \(\lfloor 10\ln(1+A)\rfloor\) 是很小的,最多只有 \(53\),因此直接枚举它,剩余部分怎么办呢?\(k\) 很小,而我们逐层转移时只用关心上一层血缘链上的点的状态,一眼状压。\(dp_{i,j,S}\) 表示考虑到第 \(i\) 层,\(j\) 对相邻异性,上一层状态为 \(S\)。转移很容易。代价跑一遍上面的最小割即可。最后再对与血缘链没有任何联系的点跑一遍最小割,把共享加上即可

时间复杂度 \(53·n^2k·4^k+53·n2^k·\Theta(\text{dinic})\),由于 dinic 部分显然跑不满,可以通过。

const int MAXK = 4;
const int MAXN = 50;
const int MAXM = 1000;
const int MAXP = 1e4;
const int MAXNK = 200;
const int MAXMSK = 16;
int n, k, m, p, cst[MAXM + 5]; ll res = 0;
int ch[MAXK + 2][MAXN + 5], dep[MAXM + 5]; char s[MAXM + 5];
struct dat {int u, v, w; double coef;} b[MAXP + 5];
struct ufset {
	int f[MAXM + 5];
	ufset() {memset(f, 0, sizeof(f));}
	int find(int x) {return (!f[x]) ? x : f[x] = find(f[x]);}
} F;
void init_dep() {
	for (int i = 1; i <= p; i++) {
		int fu = F.find(b[i].u), fv = F.find(b[i].v);
		if (dep[fv]) swap(fu, fv); if (fu != fv) F.f[fv] = fu;
	}
	for (int i = 1; i <= m; i++) dep[i] = dep[F.find(i)];
}
namespace MaxFlow {
	const ll INFll = 1e13;
	const int MAXV = 1e5;
	const int MAXE = 1e6;
	int S, T, hd[MAXV + 5], to[MAXE + 5], nxt[MAXE + 5], ec = 1; ll cap[MAXE + 5];
	void init_graph() {for (int i = 1; i <= T; i++) hd[i] = 0; ec = 1;}
	void adde(int u, int v, ll f) {
		to[++ec] = v; cap[ec] = f; nxt[ec] = hd[u]; hd[u] = ec;
		to[++ec] = u; cap[ec] = 0; nxt[ec] = hd[v]; hd[v] = ec;
	}
	int dis[MAXV + 5], now[MAXV + 5];
	bool getdep(int S, int T) {
		for (int i = 1; i <= T; i++) dis[i] = -1;
		queue<int> q; q.push(S); dis[S] = 0;
		while (!q.empty()) {
			int x = q.front(); q.pop(); now[x] = hd[x];
			for (int e = hd[x]; e; e = nxt[e]) {
				int y = to[e]; ll z = cap[e];
				if (z && !~dis[y]) {dis[y] = dis[x] + 1; q.push(y);}
			}
		}
		return ~dis[T];
	}
	int getflow(int x, ll f) {
		if (x == T) return f; ll ret = 0;
		for (int &e = now[x]; e; e = nxt[e]) {
			int y = to[e]; ll z = cap[e];
			if (z && dis[y] == dis[x] + 1) {
				ll w = getflow(y, min(f - ret, z));
				ret += w; cap[e] -= w; cap[e ^ 1] += w;
				if (f == ret) return ret;
			}
		}
		return ret;
	}
	ll dinic() {
		ll ret = 0;
		while (getdep(S, T)) ret += getflow(S, INFll);
		return ret;
	}
} using namespace MaxFlow;
ll getcst(int x, int msk, int LN) {
	int cntv = 0, cnte = 0, idcnt; static int id[MAXM + 5];
	for (int i = 1; i <= m; i++) if (dep[i] == x) id[i] = ++cntv;
	for (int i = 1; i <= p; i++) if (dep[b[i].u] == x) ++cnte;
	S = cntv + cnte * 2 + 1; T = S + 1; idcnt = cntv; ll sum = 0;
	init_graph();
	for (int i = 1; i <= m; i++) if (dep[i] == x) adde(S, id[i], cst[i]);
	for (int i = 1; i <= k; i++) {
		int pt = ch[i][x];
		if (((msk >> i - 1) & 1) == (s[pt] == 'M')) adde(S, id[pt], INFll); // not change
		else adde(id[pt], T, INFll);
	}
	for (int i = 1; i <= p; i++) if (dep[b[i].u] == x) {
		int idu = id[b[i].u], idv = id[b[i].v]; ++idcnt; // both not change
		adde(idcnt, idu, INFll); adde(idcnt, idv, INFll);
		adde(S, idcnt, b[i].w * LN); sum += b[i].w * LN;
		++idcnt; // both change
		adde(idu, idcnt, INFll); adde(idv, idcnt, INFll);
		adde(idcnt, T, floor(b[i].w * b[i].coef) * LN);
		sum += floor(b[i].w * b[i].coef) * LN;
	}
	return sum - dinic();
}
ll getrst(int LN) {
	int cntv = 0, cnte = 0, idcnt; static int id[MAXM + 5];
	for (int i = 1; i <= m; i++) if (!dep[i]) id[i] = ++cntv;
	for (int i = 1; i <= p; i++) if (!dep[b[i].u]) ++cnte;
	S = cntv + cnte * 2 + 1; T = S + 1; idcnt = cntv; ll sum = 0;
	init_graph();
	for (int i = 1; i <= m; i++) if (!dep[i]) adde(S, id[i], cst[i]);
	for (int i = 1; i <= p; i++) if (!dep[b[i].u]) {
		int idu = id[b[i].u], idv = id[b[i].v]; ++idcnt; // both not change
		adde(idcnt, idu, INFll); adde(idcnt, idv, INFll);
		adde(S, idcnt, b[i].w * LN); sum += b[i].w * LN;
		++idcnt; // both change
		adde(idu, idcnt, INFll); adde(idv, idcnt, INFll);
		adde(idcnt, T, floor(b[i].w * b[i].coef) * LN);
		sum += floor(b[i].w * b[i].coef) * LN;
	}
	return sum - dinic();
}
ll dp[MAXN + 5][MAXNK + 5][MAXMSK + 2];
void solve_LN(int LN) {
	static ll cst_layer[MAXN + 5][MAXMSK + 2];
//	printf("solve_LN %d\n", LN);
	for (int i = 1; i <= n; i++) for (int j = 0; j < (1 << k); j++)
		cst_layer[i][j] = getcst(i, j, LN);
	memset(dp, 0xcf, sizeof(dp));
	for (int i = 0; i < (1 << k); i++) dp[1][0][i] = cst_layer[1][i];
	for (int i = 2; i <= n; i++) for (int j = 0; j < (1 << k); j++) {
		for (int l = 0; l <= (i - 1) * k; l++) for (int s = 0; s < (1 << k); s++)
			if (l >= __builtin_popcount(j ^ s))
				chkmax(dp[i][l][j], dp[i - 1][l - __builtin_popcount(j ^ s)][s] + cst_layer[i][j]);
	}
	ll rst = getrst(LN);
	for (int i = 0; i <= (n - 1) * k; i++) if (floor(10.0 * log(i + 1)) == LN) {
		for (int j = 0; j < (1 << k); j++) chkmax(res, dp[n][i][j] + rst);
	}
}
int main() {
	scanf("%d%d%d%d%s", &n, &k, &m, &p, s + 1);
	for (int i = 1; i <= m; i++) scanf("%d", &cst[i]);
	for (int i = 1; i <= k; i++) for (int j = 1; j <= n; j++)
		scanf("%d", &ch[i][j]), dep[ch[i][j]] = j;
	for (int i = 1; i <= p; i++) scanf("%d%d%d%lf", &b[i].u, &b[i].v, &b[i].w, &b[i].coef);
	init_dep();
	for (int LN = 0; LN <= 53; LN++) solve_LN(LN);
	printf("%lld\n", res);
	return 0;
}
posted @ 2022-07-12 12:15  tzc_wk  阅读(50)  评论(0编辑  收藏  举报