zoukankan      html  css  js  c++  java
  • @atcoder


    @description@

    给定 N 与两个 0~N-1 的置换 P, Q。
    现在你需要找到两个置换 A 与 B,使得 A[i] = P[i] 或 i;B[i] = Q[i] 或 i。
    最大化 A[i] ≠ B[i] 的 i 的数量。 输出最大值。

    Constraints
    1 ≤ N ≤ 100000。
    保证 P 为 0~N-1 的排列。
    保证 Q 为 0~N-1 的排列。

    Input
    输入形式如下:
    N
    P0 P1 ⋯ PN−1
    Q0 Q1 ⋯ QN−1

    Output
    输出 A[i] ≠ B[i] 的 i 的最大数量。

    Sample Input 1
    4
    2 1 3 0
    0 2 3 1
    Sample Output 1
    3

    一种方案为 A=(0,1,2,3), B=(0,2,3,1)。

    Sample Input 2
    10
    0 4 5 3 7 8 2 1 9 6
    3 8 5 6 4 0 2 1 7 9
    Sample Output 2
    8

    Sample Input 3
    32
    22 31 30 29 7 17 16 3 14 9 19 11 2 5 10 1 25 18 15 24 20 0 12 21 27 4 26 28 8 6 23 13
    22 3 2 7 17 9 16 4 14 8 19 26 28 5 10 1 25 18 15 13 11 0 12 23 21 20 29 24 27 6 30 31
    Sample Output 3
    28

    @solution@

    对于置换 P,我们将其分解成若干循环。
    则对于每一个循环,要么同时选择变成 i, 要么同时保持 P[i] 不变。
    每个元素有两种选择,而选择之间会互相影响,让人不是想到 2-sat 就是想到最小割(而时限又进一步地暗示最小割!)。

    最小割的转化:我们求最小的 A[i] = B[i] 的数量。
    对于每个循环,我们建个虚点,向循环内所有点连容量为 inf 的双向边。
    剩下的就是一个经典的二元关系建图(即:同时选会产生代价之类的问题)。

    对于每个 Pi,源点连向它表示选 i,它连向汇点表示选 Pi。
    对于每个 Qi,源点连向它表示选 Qi,它连向汇点表示选 i。
    然后根据 i, Pi, Qi 之间的相等关系分类建边。具体可以看代码。
    至于为什么不对称着建,因为对称着建会产生负权边,就不大好。

    @accepted code@

    #include<queue>
    #include<cstdio>
    #include<algorithm>
    using namespace std;
    const int MAXN = 100000;
    const int INF = (1<<30);
    struct FlowGraph{
    	struct edge{
    		int to, cap, flow;
    		edge *nxt, *rev;
    	}edges[20*MAXN + 5], *adj[4*MAXN + 5], *cur[4*MAXN + 5], *ecnt;
    	FlowGraph() {ecnt = &edges[0];}
    	void addedge(int u, int v, int c1, int c2) {
    		edge *p = (++ecnt), *q = (++ecnt);
    		p->to = v, p->cap = c1, p->flow = 0;
    		p->nxt = adj[u], adj[u] = p;
    		q->to = u, q->cap = c2, q->flow = 0;
    		q->nxt = adj[v], adj[v] = q;
    		p->rev = q, q->rev = p;
    //		printf("! %d %d %d %d
    ", u, v, c1, c2);
    	}
    	int dis[4*MAXN + 5];
    	int s, t;
    	bool relabel() {
    		for(int i=0;i<=t;i++)
    			dis[i] = INF, cur[i] = adj[i];
    		queue<int>que; que.push(t), dis[t] = 0;
    		while( !que.empty() ) {
    			int f = que.front(); que.pop();
    			for(edge *p=adj[f];p;p=p->nxt)
    				if( p->rev->cap > p->rev->flow )
    					if( dis[f] + 1 < dis[p->to] )
    						que.push(p->to), dis[p->to] = dis[f] + 1;
    		}
    		return !(dis[s] == INF);
    	}
    	int aug(int x, int tot) {
    		if( x == t ) return tot;
    		int sum = 0;
    		for(edge *&p=cur[x];p;p=p->nxt) {
    			if( p->cap > p->flow && dis[p->to] + 1 == dis[x] ) {
    				int del = aug(p->to, min(p->cap - p->flow, tot - sum));
    				sum += del, p->flow += del, p->rev->flow -= del;
    				if( sum == tot ) break;
    			}
    		}
    		return sum;
    	}
    	int max_flow(int _s, int _t) {
    		int flow = 0; s = _s, t = _t;
    		while( relabel() )
    			flow += aug(s, INF);
    		return flow;
    	}
    }G;
    int P[MAXN + 5], Q[MAXN + 5];
    bool tag[MAXN + 5];
    int main() {
    	int N, cnt; scanf("%d", &N), cnt = 2*N;
    	for(int i=1;i<=N;i++)
    		scanf("%d", &P[i]), P[i]++, tag[i] = false;
    	for(int i=1;i<=N;i++)
    		if( !tag[i] ) {
    			int p = i; cnt++;
    			do {
    				tag[p] = true;
    				p = P[p];
    				G.addedge(cnt, p, INF, INF);
    			}while( p != i );
    		}
    	for(int i=1;i<=N;i++)
    		scanf("%d", &Q[i]), Q[i]++, tag[i] = false;
    	for(int i=1;i<=N;i++)
    		if( !tag[i] ) {
    			int p = i; cnt++;
    			do {
    				tag[p] = true;
    				p = Q[p];
    				G.addedge(cnt, p + N, INF, INF);
    			}while( p != i );
    		}
    	int s = 0, t = cnt + 1;
    	for(int i=1;i<=N;i++) {
    		if( i == P[i] && i == Q[i] && P[i] == Q[i] )// a = c = 1
    			G.addedge(s, i, 1, 0), G.addedge(i, t, 1, 0);
    		if( i == P[i] && i != Q[i] && P[i] != Q[i] )// b = 1
    			G.addedge(s, i + N, 1, 0);
    		if( i != P[i] && i == Q[i] && P[i] != Q[i] )// c = 1
    			G.addedge(i, t, 1, 0);
    		if( i != P[i] && i != Q[i] && P[i] == Q[i] )// e = f = 1
    			G.addedge(i, i + N, 1, 1);
    		if( i != P[i] && i != Q[i] && P[i] != Q[i] )// f = 1
    			G.addedge(i, i + N, 1, 0);
    	}//s -> i(a), s -> i+N(b), i -> t(c), i+N -> t(d), i+N -> i(e), i -> i+N(f)
    	printf("%d
    ", N - G.max_flow(s, t));
    }
    

    @details@

    怎么感觉 F 题比 E 题水啊。是我的错觉吗。

  • 相关阅读:
    php-Zip打包文件
    PHP命令行类库 climate
    vim 添加块注释
    冒泡排序|插入排序
    PHP-SeasLog安装和使用
    链表
    多线程上下文切换
    竞态与线程安全
    线程的生命周期
    线程创建的两种方法
  • 原文地址:https://www.cnblogs.com/Tiw-Air-OAO/p/11688690.html
Copyright © 2011-2022 走看看