首页 > 其他分享 >CF1773D Dominoes - 网络流 - 二分图 - 计数 -

CF1773D Dominoes - 网络流 - 二分图 - 计数 -

时间:2023-03-03 19:01:43浏览次数:67  
标签:二分 vis int LL flow Dominoes rem CF1773D now

题目链接:https://codeforces.com/problemset/problem/1773/D

题解:
首先将棋盘黑白染色,是一个二分图
由于题目保证初始状态一定能密铺,因此这个二分图一定有完美匹配
现在要铺 2 个地方,显然分两种情况:

  • 黑白颜色相同
    显然此时并不能产生完美匹配,因此这些情况都是答案,即 \(2 \times \binom{cnt}{2}\)
  • 颜色不同
    枚举黑色点删哪个,考察哪些白色点删了之后二分图就不存在完美匹配了,即求新图最大匹配的必经点
    上一篇写的是必经边,这里换成了必经点,处理有所不同
    首先,如果流量不是白色点-1,就说明无论选什么白色点,都无法达到完美匹配,答案加上白色点个数
    否则,考虑从汇点 \(T\) 反向走,每次走满流的点,如果当前点是白色点,就标记。最后统计没标记的点就是必经点。
    为什么是对的呢?
    首先,考虑 \(T\) 到某个白色点的反向边,反向边由于容量为 0,满流则说明其对应的正向边的流量也为 0,也就是这个白色点没有参加匹配
    从这个白色点开始,走反向边到黑色点,意味着这个黑点到这个白点没有匹配,可以作为备选
    然后从黑色点开始走,满流走到下一个白色点,意味着这个黑点和下一个白色点匹配上了,这时,我们发现可以将这个黑点和上一个白点匹配,也就是下一个白点不是必选点。这样递归下去我们就得到了所有非必选点,反过来就是必经点了
// by SkyRainWind
#include <bits/stdc++.h>
#define mpr make_pair
#define debug() cerr<<"Yoshino\n"
#define pii pair<int,int>
#define pb push_back

using namespace std;

typedef long long ll;
typedef long long LL;

const int inf = 1e9, INF = 0x3f3f3f3f, maxn = 3005;

int n,m,a[1005][1005];
struct ed{
	LL from,to,cap,flow,rev;
	ed(){}
	ed(LL from,LL to,LL cap,LL flow,LL rev):from(from),to(to),cap(cap),flow(flow),rev(rev){}
};
vector<ed>g[maxn];

const int dx[] = {1,-1,0,0};
const int dy[] = {0,0,1,-1};
inline int in(int x,int y){return x>=1&&x<=n&&y>=1&&y<=m;} 
inline int ind(int x,int y){return (x-1)*m+y;}

struct netflow{
	int cur[maxn]; 
	int d[maxn], q[maxn], hd, tl;
	int s, t;	// 源 汇 
	
	netflow(){s=t=-1;}
	
	void init(int s0,int t0){
		s = s0, t = t0;
	}

	void add(int x,int y,LL v){
		g[x].push_back(ed(x,y,v,0,g[y].size()));
		g[y].push_back(ed(y,x,0,0,g[x].size() - 1));
	}
	
	int bfs(){
		memset(d,0, sizeof d);
		hd = tl = 0;
		q[tl ++] = s;
		d[s] = 1;
		while(hd != tl){
			int now = q[hd ++];
			for(int i = 0;i<g[now].size();i++){
				ed &e = g[now][i];
				if(!d[e.to] && e.cap > e.flow)d[e.to] = d[now] + 1, q[tl ++] = e.to;
			}
		}
		return d[t];
	}
	
	LL dfs(int now,LL rem){	// rem 当前流量 
		if(now == t || !rem)return rem;
		LL flow = 0;
		for(int &i = cur[now]; i < g[now].size();i ++){
			ed &e = g[now][i];
				// 分层图 & 残量不为0 
			if(d[e.to] == d[now] + 1 && e.cap > e.flow){
				LL f = dfs(e.to, min(rem, e.cap - e.flow));
				rem -= f, flow += f, e.flow += f, g[e.to][e.rev].flow -= f;
			}
			if(!rem)break;
		}
		if(rem)d[now] = -1;
		return flow;
	}
	
	LL dinic(){
		assert(s!=-1);
		LL flow = 0;
		while(bfs()){
			memset(cur, 0, sizeof cur);
			flow += dfs(s, 1ll << 62);
		}
		return flow;
	}
}nf;
vector<pii>ed;
vector<int>lft,rgt; 
int vis[maxn];

void dfs(int x){
	vis[x] = 1;
	for(int i=0;i<g[x].size();i++){
		struct ed e = g[x][i];
		if(!vis[e.to] && e.flow == e.cap)dfs(e.to);
	}
}

map<int,int>mp;
int cc;

signed main(){
//	freopen("CF1773D.in","r",stdin);
	scanf("%d%d",&n,&m);
	for(int i=1;i<=n;i++){
		char s[1005];
		scanf("%s",s+1);
		for(int j=1;j<=m;j++)
			if(s[j] == '#')a[i][j] = 0;
			else a[i][j] = 1;
	}
	for(int i=1;i<=n;i++)
		for(int j=1;j<=m;j++)if(a[i][j] == 1)mp[ind(i, j)] = ++cc;
	for(int i=1;i<=n;i++)
		for(int j=1;j<=m;j++)if(a[i][j]){
			if((i%2 + j) & 1){
				for(int k=0;k<4;k++){
					int fi = i+dx[k], fj = j+dy[k];
					if(!in(fi, fj) || !a[fi][fj])continue;
					ed.pb(mpr(mp[ind(i, j)], mp[ind(fi, fj)]));
				}
			}
		}
	
	int cnt0=0, cnt1=0;
	for(int i=1;i<=n;i++)
		for(int j=1;j<=m;j++)if(a[i][j]){
			if((i%2 + j) & 1)++ cnt0, lft.pb(mp[ind(i, j)]);
			else ++cnt1, rgt.pb(mp[ind(i, j)]);
		}
	
	ll ans = 1ll*cnt0*(cnt0-1)/2 + 1ll*cnt1*(cnt1-1)/2;
	if(ans >= (ll)1e6)return printf("%d\n",(int)1e6), 0;
	
	int s = 2*cnt0+1, t = s+1;
	nf.init(s, t);
	for(int u : lft){
		for(int i : lft)g[i].clear(), vis[i] = 0;
		for(int i : rgt)g[i].clear(), vis[i] = 0;
		g[s].clear(), g[t].clear();
		vis[s] = vis[t] = 0;
		
		for(pii e : ed){
			if(e.first ^ u){
				nf.add(e.first, e.second, 1);
			}
		}
		for(int v : lft)if(u^v)nf.add(s, v, 1);
		for(int v : rgt)nf.add(v, t, 1);
		
		ll res = nf.dinic(), r=0;
		if(res != cnt0-1){
			ans += cnt1;
			continue;
		}
		dfs(t);
		for(int v : rgt)if(!vis[v])++ r;
		ans += r;
	}
	cout << min(ans, (ll)1e6) << '\n';

	return 0;
}

标签:二分,vis,int,LL,flow,Dominoes,rem,CF1773D,now
From: https://www.cnblogs.com/SkyRainWind/p/17176667.html

相关文章

  • Luogu3731 新型城市化 - 二分图 - 网络流 - 强连通分量 -
    题目链接:https://www.luogu.com.cn/problem/P3731题解:考虑原图的补图,因为题目中保证了城市群最多有两个,因此补图是一个二分图,城市群等价于独立集原题转化成了,删去一条边......
  • hdu-2063 二分图
    http://acm.hdu.edu.cn/showproblem.php?pid=2063过山车TimeLimit:1000/1000MS(Java/Others)    MemoryLimit:32768/32768K(Java/Others)TotalSubmi......
  • 牛客小白月赛67—— 一刀二分三角(数学)
    https://ac.nowcoder.com/acm/contest/51458/C题目大意:给定一个三角形,三个点分别是(0,0)(xc,yc)(xb,0)。​问我们是否可以将三角形沿着x=某个数字切开,得到的两个平面图形面......
  • Leetcode——二分法bisect_left,bisect_right
    !前提——列表有序case1如果列表中没有元素x,那么bisect_left(ls,x)和bisec_right(ls,x)返回相同的值,该值是x在ls中“合适的插入点索引,使得数组有序”。此时,ls[index2]......
  • 代码随想录算法训练营第一天| 704. 二分查找、27. 移除元素。
    LeetCode704-二分查找题目描述:给定一个 n 个元素有序的(升序)整型数组 nums 和一个目标值 target ,写一个函数搜索 nums 中的 target,如果目标值存在返回下标,否则返......
  • 基本功练习_2_27_之二分法查找
    #include<stdio.h>intsearch(int*a,intkey,intlow,inthigh){intmid;if(low>high)return-1;mid=(low+high)/2;if(a[mid]==key)returnmid;elseif(a[m......
  • 算法基础1.2.2浮点数二分
    前言直接先把整数二分看完看整数二分文章点这里现在只需要补充几个特点(其实整数二分的博客的前言就介绍了一些)就可以了由于浮点数二分没有向下取整的特性(不懂的话就去......
  • 基本算法之二分查找法折半查找(Java)
    前提条件:数组中的数据必须是有序的!核心思想:每次排除一半的数据,查询数据的性能明显提高很多!      publicclassTask{publicstaticvoidmain(Stri......
  • 算法基础1.2.1整数二分
    前言如果第一次接触二分其实很难理解它的含义我对二分的理解就是找到一个条件,能够保证所有数据对于这个条件要么是True要么是False。二分的作用是查找。二分本质不是单......
  • leetcode之——二分法模板
    classSolution:defsearch(self,nums:List[int],target:int)->int:n=len(nums)left,right=0,n-1whileleft<=right:k=(right-left)//2+left......