[CF1517G]Starry Night Camping

27 篇文章 0 订阅

题目

传送门 to CF

思路

首先,这东西稍微有一点 constructive \text{constructive} constructive,所以我未必能想到;其次,按照我愚蠢的方法, 100 % 100\% 100% 的想不到。

原本我以为,可以最大权闭合子图乱搞;但是这里的权值需要设置为负数(四个点之中有一个点不保留,就可以获得大量权值;这是一个负数的割),计划泡汤。我又联想到了黑白染色。

我一想:“ x i , y i x_i,y_i xi,yi 均为偶数,不就是所有的黑点嘛!” 于是开始了轰轰烈烈的尝试,然后以失败告终。

直到看到题解,我才发现:原来 x i , y i x_i,y_i xi,yi 均为偶数,并不是 2 ∣ ( x i + y i ) 2\mid(x_i+y_i) 2(xi+yi) 啊,长见识了!

回到正题上。首先这是线性规划;于是考虑网络流。虽然最大权闭合子图是错误的,但是我们有别的方法让它正确。记 f ( x , y ) f(x,y) f(x,y) 为,是否 ( x , y ) (x,y) (x,y) 存在点,且左侧和右侧至少有一个点。那么存在平行四边形,等价于 f ( x , y ) f(x,y) f(x,y) f ( x , y + 1 ) ∨ f ( x , y − 1 ) f(x,y{\rm+}1)\vee f(x,y{\rm-}1) f(x,y+1)f(x,y1),其中 2 ∣ x ,    2 ∣ y 2\mid x,\;2\mid y 2x,2y

那么,只需考虑删点对于 f ( x , y )    ( 2 ∣ x ) f(x,y)\;(2\mid x) f(x,y)(2x) 的影响。可以发现,其左右的两个点都可以为它 “供能”,想要它为 false \texttt{false} false,要么左右两侧都不选,要么自己不选。这是简单的网络流模型:左右两侧的点都向新点 t t t + ∞ +\infty + 容量的边(若有一个是 S S S 部,则 t t t S S S 部),然后 t t t 向当前点连边,容量是 w x w_x wx,即自己不选时(此边被割掉)无论左右如何自己都可以是 T T T 部。

由于 2 ∣ y 2\mid y 2y 2 ∤ y 2\nmid y 2y 互不影响,两边分别如此建图。问题则变为 f ( x , y ) f(x,y) f(x,y) f ( x , y + 1 ) f(x,{y{\rm+}1}) f(x,y+1) 不能同时为 true \texttt{true} true 。现在总算可以黑白染色了:将 2 ∣ y 2\mid y 2y S , T S,T S,T 状态翻转,问题则变为,某种二者取值不同的情况是被禁止的。连 + ∞ +\infty + 容量的边,表示不可接受的代价,就行了。

重新审视网络流图,你会发现:实际上我们建出了这样的一个图: ( 1 , 1 ) → ( 0 , 1 ) → ( 0 , 0 ) → ( 1 , 0 ) (1,1)\to(0,1)\to(0,0)\to(1,0) (1,1)(0,1)(0,0)(1,0),其中 0 , 1 0,1 0,1 代表奇偶性,并且每个点拆成了入度和出度。那么这就很 constructive \text{constructive} constructive 了:它实际上揭示了一种将平行四边形拆解成路径的方式,而且这个路径也总是对应平行四边形。

题解多数都是上面的这种 constructive \text{constructive} constructive 讲法,包括官方题解。这谁能想得到嘛 😠💢

可是这跟我连奇偶都分不清有什么关系呢。时间复杂度 O ( n 3 ) \mathcal O(n^3) O(n3),即 O ( V 2 E ) \mathcal O(V^2E) O(V2E) 的网络流。

代码

#include <cstdio>
#include <iostream>
#include <algorithm>
#include <cstring>
#include <cctype>
#include <map>
using namespace std;
# define rep(i,a,b) for(int i=(a); i<=(b); ++i)
# define drep(i,a,b) for(int i=(a); i>=(b); --i)
typedef long long llong;
inline int readint(){
	int a = 0, c = getchar(), f = 1;
	for(; !isdigit(c); c=getchar())
		if(c == '-') f = -f;
	for(; isdigit(c); c=getchar())
		a = (a<<3)+(a<<1)+(c^48);
	return a*f;
}

const int MAXN = 2005, INF = 0x3fffffff;
struct Edge{
	int to, nxt, val;
	Edge(int _t,int _n,int _v)
		:to(_t),nxt(_n),val(_v){}
	Edge() = default;
};
Edge e[MAXN<<2];
int head[MAXN], cntEdge;
void addEdge(int a,int b,int v){
	e[cntEdge] = Edge(b,head[a],v);
	head[a] = cntEdge ++;
	e[cntEdge] = Edge(a,head[b],0);
	head[b] = cntEdge ++;
}

int dis[MAXN];
bool bfs(const int source,const int sink){
	static int q[MAXN]; // queue
	int *fro = q, *bac = q+1;
	memset(dis+1,-1,sink<<2);
	*bac = source, dis[*bac] = 0;
	for(int x; fro!=bac; ){
		++ fro, x = *fro;
		for(int i=head[x]; ~i; i=e[i].nxt)
			if(e[i].val && !(~dis[e[i].to])){
				dis[e[i].to] = dis[x]+1;
				++ bac, *bac = e[i].to;
			}
	}
	return dis[sink] != -1;
}
int cur[MAXN];
int dfs(int x,int inFlow,const int sink){
	int sum = 0; if(x == sink) return inFlow;
	for(int &i=cur[x]; ~i; i=e[i].nxt)
		if(e[i].val && dis[e[i].to] == dis[x]+1){
			int d = dfs(e[i].to,min(inFlow-sum,e[i].val),sink);
			e[i].val -= d, e[i^1].val += d, sum += d;
			if(sum == inFlow) break; // no more flow
		}
	if(sum != inFlow) dis[x] = -1;
	return sum;
}
llong dinic(const int source,const int sink){
	long long res = 0;
	while(bfs(source,sink)){
		memcpy(cur+1,head+1,sink<<2);
		for(int &i=cur[source]; ~i; i=e[i].nxt)
			if(e[i].val) res += dfs(e[i].to,e[i].val,sink);
	}
	return res;
}

int x[MAXN], y[MAXN];
map<pair<int,int>,int> mp;
int main(){
	int n = readint(); llong ans = 0;
	const int source = (n<<1)|1, sink = source+1;
	memset(head+1,-1,sink<<2);
	rep(i,1,n){
		x[i] = readint(), y[i] = readint();
		mp[make_pair(x[i],y[i])] = i;
		int w = readint(); ans += w;
		addEdge(i+n,i,w); // if cut
	}
	rep(i,1,n)
		if((x[i]^y[i]^1)&1){
			rep(d,-1,1) if(d && mp.count(make_pair(x[i]+d,y[i])))
				addEdge(i,mp[make_pair(x[i]+d,y[i])]+n,INF);
			if(x[i]&y[i]&1) addEdge(source,i+n,INF);
		}
		else if(!(x[i]&1) && (y[i]&1)){
			rep(d,-1,1) if(d && mp.count(make_pair(x[i],y[i]+d)))
				addEdge(i,mp[make_pair(x[i],y[i]+d)]+n,INF);
		}
		else if((x[i]&1) && !(y[i]&1)) addEdge(i,sink,INF);
	printf("%lld\n",ans-dinic(source,sink));
	return 0;
}

后记

如果说思维就是大脑皮层的电信号,那么定义电阻率的比值 ρ O n e I n D a r k ρ D D ( X Y X ) = sb ⁡ ( t ) \frac{\rho_{\sf OneInDark}}{\rho_{\sf DD(XYX)}}=\operatorname{sb}(t) ρDD(XYX)ρOneInDark=sb(t) O n e I n D a r k \sf OneInDark OneInDark 的废物指数(随时间变化的函数)。在 2005 2005 2005 年,青年数学家戴伟鸥和严逸袅先后分别完成了对于 sb ⁡ ( t ) \operatorname{sb}(t) sb(t) 任意阶导数单增、 sb ⁡ ( t ) ≫ ω 100 t    ( ω ≫ 1 0 1 0 10 ) \operatorname{sb}(t)\gg\omega^{100t}\;(\omega\gg 10^{10^{10}}) sb(t)ω100t(ω101010) 的证明。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值