zoukankan      html  css  js  c++  java
  • ACL Contest 1 F Center Rearranging【分析性质、2-SAT】

    传送门

    给定 ([n] imes 3) 的两个排列 (A,B),每次操作可以选择 (xin[1,n]),将 (A) 的三个 (x) 中间那个移到开头/结尾。

    求将 (A) 变为 (B) 的最小操作次数。(nle 33)


    我们设 (A) 中最后一次移动是 push_front 的元素记作 (L),是 push_back 的元素记作 (R),没有动过的元素记作 (M)。则最终 (A) 的状态形如 (Lcdots LMcdots MRcdots R)(O(n^2)) 枚举分界点就可以确定每个元素被移动的情况。

    对于每个数 (xin[1,n]),考虑 (B)(x) 的状态对应回 (A)(x) 的位置,则有

    我们知道除了 LMR 这种情况之外,都可以确定 (A) 中与其对应的 M

    先找到已知对应的 M,我们知道它们的顺序不变,否则无解。

    假设现在已经确定了 LMR 中对应的 M,考虑如何构造操作:显然 Other 中的元素操作顺序无关,Group 1 的元素第三个要比第一个先动,Group 2 中的元素第一个要比第三个先动,然后最终序列中右边的 (L) 比左边的 (L) 先动,左边的 (R) 比右边的 (R) 先动。

    这是一个必要条件,感性理解一下它是充分的(

    如果把确定先动的向后动的连边,求一个拓扑序即为可能的操作序列。

    然而拓扑排序很难做,我们考虑直接判环。由于这个图长得很好看,它如果有环那么一定有环是形如某个 Group 1 的第 1 和 3 个位置都比某个 Group 2 的第 1 和 3 个位置靠后,形成一个 (4) 个点的环。

    LMR 的两种情况作为变量取值,转化为 2-SAT 判可行。总时间复杂度 (O(n^4))

    #include<bits/stdc++.h>
    #define PB emplace_back
    #define fi first
    #define se second
    using namespace std;
    typedef pair<int, int> pii;
    template<typename T>
    void read(T &x){
    	int ch = getchar(); x = 0;
    	for(;ch < '0' || ch > '9';ch = getchar());
    	for(;ch >= '0' && ch <= '9';ch = getchar()) x = x * 10 + ch - '0';
    } template<typename T>
    bool chmin(T &a, const T &b){if(a > b) return a = b, 1; return 0;}
    int cnt, head[70], to[2500], nxt[2500], col[70], cnum, stk[70], tp, dfn[70], low[70], tim;
    bool ins[70];
    void add(int u, int v){to[++cnt] = v; nxt[cnt] = head[u]; head[u] = cnt;}
    void dfs(int x){
    	dfn[x] = low[x] = ++tim; stk[++tp] = x; ins[x] = true;
    	for(int i = head[x];i;i = nxt[i])
    		if(!dfn[to[i]]){
    			dfs(to[i]); chmin(low[x], low[to[i]]);
    		} else if(ins[to[i]]) chmin(low[x], dfn[to[i]]);
    	if(low[x] == dfn[x]){ ++ cnum;
    		do {
    			col[stk[tp]] = cnum;
    			ins[stk[tp]] = false;
    		} while(stk[tp--] != x);
    	}
    }
    int n, op[34], ans = -1;
    vector<int> pa[34], pb[34];
    vector<pii> mat;
    bool calc(int L, int R){
    	memset(head, 0, sizeof head); memset(op, 0, sizeof op); mat.resize(0);
    	cnt = cnum = tp = tim = 0; memset(dfn, 0, sizeof dfn); memset(col, 0, sizeof col);
    	auto get = [&](int x) -> string {return x <= L ? "L" : (x >= R ? "R" : "M");};
    	auto check = [&](int a, int b) -> bool {return pb[a][0] >= pb[b][0] && pb[a][2] >= pb[b][2];};
    	for(int i = 1;i <= n;++ i){
    		string str = get(pb[i][0]) + get(pb[i][1]) + get(pb[i][2]);
    		if(str == "LLL" || str == "RRR") return false;
    		if(str == "LLR") op[i] = 1;
    		else if(str == "LRR") op[i] = 2;
    		else if(str == "LMR") op[i] = 3;
    		else if(str == "MMR"){mat.PB(pa[i][0], pb[i][0]); mat.PB(pa[i][2], pb[i][1]);}
    		else if(str == "LMM"){mat.PB(pa[i][0], pb[i][1]); mat.PB(pa[i][2], pb[i][2]);}
    		else if(str == "MMM") for(int j = 0;j < 3;++ j) mat.PB(pa[i][j], pb[i][j]);
    		else if(str == "MRR") mat.PB(pa[i][0], pb[i][0]);
    		else if(str == "LLM") mat.PB(pa[i][2], pb[i][2]);
    	} sort(mat.begin(), mat.end());
    	for(int i = 1;i < mat.size();++ i) if(mat[i].se < mat[i-1].se) return false;
    	for(int i = 1;i <= n;++ i) if(op[i] == 1)
    		for(int j = 1;j <= n;++ j) if(op[j] == 2 && check(i, j)) return false;
    	for(int i = 1;i <= n;++ i) if(op[i] == 3){
    		int fbd = 0;
    		for(pii &o : mat){
    			bool flg = o.se < pb[i][1];
    			if((o.fi < pa[i][0]) != flg) fbd |= 1;
    			if((o.fi < pa[i][2]) != flg) fbd |= 2;
    		} for(int j = 1;j <= n;++ j)
    			if(op[j] == 1){if(check(j, i)) fbd |= 2;}
    			else if(op[j] == 2){if(check(i, j)) fbd |= 1;}
    			else if(j > i && op[j] == 3){
    				bool flg = pb[i][1] < pb[j][1];
    				if(check(i, j) || (pa[i][0] < pa[j][2]) != flg){
    					add(i, j); add(j+n, i+n);
    				} if(check(j, i) || (pa[i][2] < pa[j][0]) != flg){
    					add(i+n, j+n); add(j, i);
    				} if((pa[i][0] < pa[j][0]) != flg){
    					add(i, j+n); add(j, i+n);
    				} if((pa[i][2] < pa[j][2]) != flg){
    					add(i+n, j); add(j+n, i);
    				}
    			}
    		if(fbd == 3) return false;
    		if(fbd == 1) add(i, i+n); else if(fbd == 2) add(i+n, i);
    	} for(int i = 1;i <= n;++ i) if(op[i] == 3){
    		if(!dfn[i]) dfs(i); if(!dfn[i+n]) dfs(i+n);
    		if(col[i] == col[i+n]) return false;
    	} return true;
    }
    int main(){ read(n);
    	for(int i = 1, x;i <= 3*n;++ i){read(x); pa[x].PB(i);}
    	for(int i = 1, x;i <= 3*n;++ i){read(x); pb[x].PB(i);}
    	for(int l = 0;l <= 3*n;++ l)
    		for(int r = l+1;r <= 3*n+1;++ r)
    			if(r-l-1 > ans && calc(l, r)) ans = r-l-1;
    	printf("%d
    ", ~ans ? 3*n-ans : -1);
    }
    

    从下午开始写,调到了晚上 7 点,人都傻了(坑:

    1. 要分清楚两个数组(
    2. 不能想当然地写 if-else就这样还能过 3/4 的点?
  • 相关阅读:
    (转)Dockerfile安全介绍
    (转)浅析Docker运行安全
    (转)网络安全设备默认密码
    docker kali 相关初始化工作
    hibernate(结合springboot)入门
    idea 通过 Generate POJOs.groovy 生成实体类
    linux jdk 安装
    docker pstgres安装
    使用docker搭建FastDFS文件系统
    [docker]离线环境导入镜像
  • 原文地址:https://www.cnblogs.com/AThousandMoons/p/14612168.html
Copyright © 2011-2022 走看看