• AtCoder AGC038F Two Permutations (网络流、最小割)


    题目链接

    https://atcoder.jp/contests/agc038/tasks/agc038_f

    题解

    好题。
    首先观察到一个性质,对于排列(P), 其所形成的每个轮换中的点(A_i)是选(i)还是选(P_i)的状态必须相同。(Q_i)同理。
    然后转化成最小化(A_i=B_i)的位置(i)数量。
    考虑(A_i=B_i)的条件:
    (1) (P_i=Q_i=i), 则此位置无用,(A_i=B_i)一定满足。
    (2) (P_i=i, Q_i e i), 则(A_i=B_i)等价于(B_i=i).
    (3) (P_i e i, Q_i=i), 则(A_i=B_i)等价于(A_i=i).
    (4) (P_i=Q_i e i), 则(A_i=B_i)等价于(A_i)(B_i)选的状态(即是(i)还是(P_i)(Q_i))相同。
    (5) (P_i e Q_i e i), 则(A_i=B_i)等价于(A_i=i)(B_i=i).

    那么我们可以从中观察到一个集合划分模型: 把所有点划分为(S,T)两个集合,设(A_i=i)表示(A_i)(S)集,(A_i=P_i)表示(A_i)(T)集,(B_i=i)表示(i)(T)集,(B_i=Q_i)表示(i)(S)集。那么上述条件就可以转化为:
    对每个位置(i):
    (1) (P_i=Q_i), 则此位置无用,一定要花费(1)的代价。
    (2) (P_i=i, Q_i e i), 则如果(B_i)(T)集需要花费(1)的代价。((S)连到(Q_i))
    (3) (P_i e i, Q_i=i), 则如果(A_i)(S)集需要花费(1)的代价。((P_i)连到(T))
    (4) (P_i=Q_i e i), 则如果(A_i)(B_i)在不同的集合要花费(1)的代价。((P_i)(Q_i)连双向)
    (5) (P_i e Q_i e i), 则如果(A_i)(S)集且(B_i)(T)集需要花费(1)的代价。((P_i)连到(Q_i))

    于是,给每个轮换建立一个点,再对每个(i)分类讨论,将其所对应的轮换连边即可。
    由于建出来的图是二分图,且边权都是(1), 故时间复杂度(O(nsqrt n)).

    我写完之后一直TLE, 最后发现居然是我写了两年的当前弧优化一直是个假的……挂了38发真的是很无语……

    代码

    #include<cstdio>
    #include<cstdlib>
    #include<iostream>
    #include<cstring>
    using namespace std;
    
    const int INF = 1e7;
    
    namespace NetFlow
    {
    	const int N = 2e5+2;
    	const int M = 4e5;
    	struct Edge
    	{
    		int v,w,nxt,rev;
    	} e[(M<<1)+3];
    	int fe[N+3];
    	int te[N+3];
    	int dep[N+3];
    	int que[N+3];
    	int n,en,s,t;
    	void addedge(int u,int v,int w)
    	{
    //		printf("addedge %d %d %d
    ",u,v,w);
    		en++; e[en].v = v; e[en].w = w;
    		e[en].nxt = fe[u]; fe[u] = en; e[en].rev = en+1;
    		en++; e[en].v = u; e[en].w = 0;
    		e[en].nxt = fe[v]; fe[v] = en; e[en].rev = en-1;
    	}
    	bool bfs()
    	{
    		for(int i=1; i<=n; i++) dep[i] = 0;
    		int head = 1,tail = 1; que[1] = s; dep[s] = 1;
    		while(head<=tail)
    		{
    			int u = que[head]; head++;
    			for(int i=fe[u]; i; i=e[i].nxt)
    			{
    				int v = e[i].v;
    				if(e[i].w>0 && dep[v]==0)
    				{
    					dep[v] = dep[u]+1;
    					if(v==t)return true;
    					tail++; que[tail] = v;
    				}
    			}
    		}
    		return false;
    	}
    	int dfs(int u,int cur)
    	{
    		if(u==t||cur==0) {return cur;}
    		int rst = cur;
    		for(int &i=te[u]; i; i=e[i].nxt)
    		{
    			int v = e[i].v;
    			if(e[i].w>0 && rst>0 && dep[v]==dep[u]+1)
    			{
    				int flow = dfs(v,min(rst,e[i].w));
    				if(flow>0)
    				{
    					e[i].w -= flow;	
    					rst -= flow;
    					e[e[i].rev].w += flow;
    					if(rst==0) {return cur;}
    				}
    			}
    		}
    		if(rst==cur) {dep[u] = -2;}
    		return cur-rst;
    	}
    	int dinic(int _n,int _s,int _t)
    	{
    		n = _n,s = _s,t = _t;
    		int ret = 0;
    		while(bfs())
    		{
    			for(int i=1; i<=n; i++) te[i] = fe[i];
    			memcpy(te,fe,sizeof(int)*(n+1));
    			ret += dfs(s,INF);
    		}
    		return ret;
    	}
    }
    using NetFlow::addedge;
    using NetFlow::dinic;
    
    const int N = 1e5;
    int a[N+3],b[N+3];
    int ca[N+3],cb[N+3];
    int n,cnta,cntb;
    
    int main()
    {
    	scanf("%d",&n);
    	for(int i=1; i<=n; i++) scanf("%d",&a[i]),a[i]++;
    	for(int i=1; i<=n; i++) scanf("%d",&b[i]),b[i]++;
    	for(int i=1; i<=n; i++)
    	{
    		if(!ca[i])
    		{
    			cnta++;
    			int x = i;
    			do
    			{
    				ca[x] = cnta;
    				x = a[x];
    			} while(x!=i);
    		}
    	}
    	for(int i=1; i<=n; i++)
    	{
    		if(!cb[i])
    		{
    			cntb++;
    			int x = i;
    			do
    			{
    				cb[x] = cntb;
    				x = b[x];
    			} while(x!=i);
    		}
    	}
    //	printf("ca: "); for(int i=1; i<=n; i++) printf("%d ",ca[i]); puts("");
    //	printf("cb: "); for(int i=1; i<=n; i++) printf("%d ",cb[i]); puts("");
    	int ans = n;
    	for(int i=1; i<=n; i++)
    	{
    		if(a[i]==i && b[i]==i) {ans--;}
    		else if(a[i]==i) {addedge(1,cb[i]+cnta+2,1);}
    		else if(b[i]==i) {addedge(ca[i]+2,2,1);}
    		else
    		{
    			addedge(ca[i]+2,cb[i]+cnta+2,1);
    			if(a[i]==b[i]) {addedge(cb[i]+cnta+2,ca[i]+2,1);}
    		}
    	}
    	ans -= dinic(2+cnta+cntb,1,2);
    	printf("%d
    ",ans);
    	return 0;
    }
    
  • 相关阅读:
    spring异常处理器
    热部署环境下,dubbo序列化的bug和优化
    函数式编程的一些魅力
    常用的jvm命令
    JS直接访问WebService(来自于网络)
    AjaxControlToolkit使用报错
    Server Application Unavailable
    ScriptManager调用WebService注意事项
    SNK签名文件添加
    vs无法调试,启动后类似于ctrl+F5
  • 原文地址:https://www.cnblogs.com/suncongbo/p/11583944.html
Copyright © 2020-2023  润新知