zoukankan      html  css  js  c++  java
  • LOJ#6068. 「2017 山东一轮集训 Day4」棋盘 题解

    题目链接

    考虑把一段连续的空地缩起来,形成一些"行"和"列"。

    考虑一个网络流模型。

    S -> 行,流量无限制,费用 (f(flow) = frac{flow (flow+1)}{2}) , 列 -> T , 流量无限制,费用 (f(flow) = frac{flow (flow+1)}{2})

    对于每个非障碍点,所在行 -> 列,流量为 1 ,费用为 0。

    至于这个 (f(flow) = frac{flow (flow+1)}{2}) , 我们把它拆成一堆流量为 1 , 费用分别为 0,1,2,3,4,...,5 的边。

    最后因为 S -> 行不会用超过这一"行"里的点数的流量,列 -> T 类似,所以我们只会连 (Theta (n^2)) 条边。

    然后直接増广即可,注意到询问很多,要从小到大排序之后增加流量。

    复杂度 (O(EK(n^2,n^2))) , 实测 ZKW 跑的很慢,因为询问可以覆盖满 (1) (cdots) (n^2),这时候显然比 EK 慢。

    code :

    #include <bits/stdc++.h>
    using namespace std;
    template <typename T> void read(T &x){
    	static char ch; x = 0,ch = getchar();
    	while (!isdigit(ch)) ch = getchar();
    	while (isdigit(ch)) x = x * 10 + ch - '0',ch = getchar();
    }
    inline void write(int x){if (x > 9) write(x/10); putchar(x%10+'0'); }
    inline bool Get(){
    	static char ch; ch = getchar();
    	while (ch != '#' && ch != '.') ch = getchar();
    	return ch == '.';
    }
    const int N = 52,M = 10005,V = N * N * 2 + 5,E = (N * N * 3 + 5) << 1;
    int To[E],Ne[E],Flow[E],Cost[E],He[V],Now[V],_ = 1;
    inline void add(int x,int y,int flow,int cost){
    	++_; To[_] = y,Flow[_] = flow,Cost[_] = cost,Ne[_] = He[x],He[x] = _;
    	++_; To[_] = x,Flow[_] = 0,Cost[_] = -cost,Ne[_] = He[y],He[y] = _;
    }
    int n,mp[N][N],id1[N][N],id2[N][N];
    int S,T,cntv,nowcost,dis[V]; bool inq[V]; queue<int>Q;
    inline bool Spfa(){
    	int i,x,y,p;
    	for (i = 1; i <= cntv; ++i) dis[i] = 10000000; dis[S] = 0,Q.push(S),inq[S] = 1;
    	while (!Q.empty()){
    		x = Q.front(),Q.pop(),inq[x] = 0;
    		for (p = He[x]; p ; p = Ne[p]) if (Flow[p] && dis[y=To[p]] > dis[x] + Cost[p]){
    			dis[y] = dis[x] + Cost[p]; if (!inq[y]) inq[y] = 1,Q.push(y);
    		}
    	}
    	return dis[T] < 10000000;
    }
    bool vis[V];
    inline int DFS(int x,int flow){
    	if (!flow) return 0;
    	if (x == T){ vis[T] = 1; return flow; }
    	if (vis[x]) return 0; vis[x] = 1;
    	int rest = flow,y,p,f;
    	for (p = Now[x]; p ; p = Ne[p]){
    		Now[x] = p;
    		if (Flow[p] && dis[y=To[p]] == dis[x] + Cost[p]){
    			f = DFS(y,min(Flow[p],rest)),Flow[p] -= f,Flow[p^1] += f,rest -= f;
    			if (!rest) return flow;
    		}
    	}
    	return flow - rest;
    }
    inline void work(int f){
    	int ff;
    	while (f && Spfa()){
    		memcpy(Now,He,cntv+1<<2),vis[T] = 1;
    		while (f && vis[T]) memset(vis,0,cntv+1),ff = DFS(S,f),nowcost += ff * dis[T],f -= ff;
    	}
    }
    int cnt[N*N*2];
    struct Query{ int id,x; bool operator < (const Query w) const{ return x < w.x; } }ask[M]; int ans[M],m;
    int main(){
    	int i,j;
    	read(n); for (i = 1; i <= n; ++i) for (j = 1; j <= n; ++j) mp[i][j] = Get(); 
    	read(m); for (i = 1; i <= m; ++i) ask[i].id = i,read(ask[i].x); sort(ask+1,ask+m+1);
    	S = 1,T = cntv = 2;
    	for (i = 1; i <= n; ++i) for (j = 1; j <= n; ++j) if (mp[i][j]){
    		id1[i][j] = id1[i-1][j] ? id1[i-1][j] : ++cntv;
    		id2[i][j] = id2[i][j-1] ? id2[i][j-1] : ++cntv;
    		add(S,id1[i][j],1,cnt[id1[i][j]]++);
    		add(id2[i][j],T,1,cnt[id2[i][j]]++);
    		add(id1[i][j],id2[i][j],1,0);
    	}
    	int nowf = 0;
    	for (i = 1; i <= m; ++i) work(ask[i].x-nowf),nowf = ask[i].x,ans[ask[i].id] = nowcost;
    	for (i = 1; i <= m; ++i) write(ans[i]),putchar('
    ');
    	return 0;
    }
    
  • 相关阅读:
    备忘录
    中缀表达式转为后缀表达式
    未出现的最小正整数
    摩尔投票算法
    两个等长升序序列找中位数
    Morris二叉树遍历
    2020牛客寒假算法基础集训营5 街机争霸
    2020牛客寒假算法基础集训营5 牛牛战队的比赛地
    2020牛客寒假算法基础集训营2 求函数
    2020牛客寒假算法基础集训营2 建通道
  • 原文地址:https://www.cnblogs.com/s-r-f/p/13736130.html
Copyright © 2011-2022 走看看