zoukankan      html  css  js  c++  java
  • 【题解】 CF1481D AB Graph 构造+图论

    Legend

    给定一张有向完全图(比竞赛图还多一倍的边),每条边有 (a)(b) 字样,问图中是否存在一条长 (m) 的回文路径。

    Link ( extrm{to Codeforces})

    Editorial

    已经发现一个更简单做法,如不想看麻烦做法,可以跳到 Editorial 2

    很容易想到,如果存在一条边,它与它的反边相同,那就一直走这两条就行了。

    • 现在图变成正边和反边权值不同了!

    我们只看为 (a) 的边,很容易想到,如果成环,一直沿着环走就行了。

    • 所以现在图中没有环了!(因为 (a) 有环,其反图 (b) 必然有环;(a) 没环,(b) 也必然没环)
    • 也就是现在图中是一个 DAG!

    接下来我们再来看比较 trivial 的部分,(m) 为奇数的是时候怎么做?

    • 显然随便 (1 o 2 o 1 o 2 o cdots o 2) 就做完了!

    那么 (m) 为偶数呢?

    看看我们现在能做什么?

    • 找到最长链,我们就能走出至多 (n-1) 个连续的 (a)(b)

    所以大概可以构造一个形如 (ababab|bababa) 的东西,转化成答案序列就是 (1n1n1n2|1n1n1n) 这样的。

    或者是 (ababa|ababa),也就是 (1n1n1(n-1)|n1n1n)

    那么什么时候这样子的构造不合法呢?

    • (n=2)!因为这个时候我们只能走出最长 (1)(a)(b)

    于是我们做完啦!

    Code

    虽然感觉我这个分析题目的方式挺正常的,但是这个代码长度为啥就这么长了呢。。。。。

    讲几个实现细节,我是怎么找到最长链的——

    考虑图是一个竞赛图而且是 DAG,所以我们就按出度排序,就是拓扑序了。

    #include <bits/stdc++.h>
    
    #define debug(...) fprintf(stderr ,__VA_ARGS__)
    #define __FILE(x)
    	freopen(#x".in" ,"r" ,stdin);
    	freopen(#x".out" ,"w" ,stdout)
    #define LL long long
    
    const int MX = 1000 + 23;
    const LL MOD = 998244353;
    
    int read(){
    	char k = getchar(); int x = 0;
    	while(k < '0' || k > '9') k = getchar();
    	while(k >= '0' && k <= '9') x = x * 10 + k - '0' ,k = getchar();
    	return x;
    }
    
    int head[MX] ,tot = 1;
    struct edge{
    	int node ,next;
    }h[MX * MX];
    void addedge(int u ,int v){
    	h[++tot] = (edge){v ,head[u]} ,head[u] = tot;
    }
    
    char E[MX][MX];
    int vis[MX] ,inq[MX] ,stk[MX] ,dep;
    int D[MX];
    
    std::vector<int> cyc;
    int getcycle(int x){
    	if(vis[x]) return 0;
    	stk[++dep] = x;
    	inq[x] = vis[x] = 1;
    	for(int i = head[x] ,d ; i ; i = h[i].next){
    		d = h[i].node;
    		if(inq[d]){
    			cyc.push_back(d);
    			while(stk[dep] != d){
    				cyc.push_back(stk[dep--]);
    			}
    			return 1;
    		}
    		if(getcycle(d)) return 1;
    	}
    	--dep;
    	inq[x] = 0;
    	return 0;
    }
    
    bool cmp(int a ,int b){return D[a] > D[b];}
    int T ,test;
    void solve(){
    	cyc.clear();
    	tot = 0;
    	memset(vis ,0 ,sizeof vis);
    	memset(inq ,0 ,sizeof inq);
    	dep = 0;
    	memset(D ,0 ,sizeof D);
    	memset(head ,0 ,sizeof head);
    	int n = read() ,m = read();
    	for(int i = 1 ; i <= n ; ++i){
    		scanf("%s" ,E[i] + 1);
    	}
    
    	for(int i = 1 ; i <= n ; ++i){
    		for(int j = i + 1 ; j <= n ; ++j){
    			if(E[i][j] == E[j][i]){
    				puts("YES");
    				for(int k = 1 ; k <= m + 1 ; ++k)
    					printf("%d%c" ,k & 1 ? i : j ," 
    "[k == m + 1]);
    				return ;
    			}
    		}
    	}
    	for(int i = 1 ; i <= n ; ++i)
    		for(int j = 1 ; j <= n ; ++j)
    			if(E[i][j] == 'a'){
    				++D[i];
    				addedge(i ,j);
    			}
    	for(int i = 1 ; i <= n ; ++i){
    		if(getcycle(i)){
    			puts("YES");
    			for(int j = 1 ; j <= m + 1 ; ++j){
    				printf("%d%c" ,cyc[j % cyc.size()] ," 
    "[j == m + 1]);
    			}
    			return ;
    		}
    	}
    	// 没找到环啊,那就是个 DAG 呗
    	if(m & 1){
    		puts("YES");
    		for(int i = 1 ; i <= m + 1 ; ++i)
    			printf("%d%c" ,(i & 1) + 1 , " 
    "[i == m + 1]);
    	}
    	else if(n >= 3){
    		puts("YES");
    		std::vector<int> orz;
    		for(int i = 1 ; i <= n ; ++i) orz.push_back(i);
    		std::sort(orz.begin() ,orz.end() ,cmp);
    
    		int last = orz[0];
    		printf("%d " ,last);
    		for(int i = 1 ; i <= m / 2 ; ++i){
    			int cur = 0;
    			if(i + 1 > m / 2){
    				if(i & 1){ // is a
    					cur = orz[n - 2];
    				}
    				else{ // is b
    					cur = orz[1];
    				}
    			}
    			else{
    				if(i & 1) cur = orz[n - 1];
    				else cur = orz[0];
    			}	
    			printf("%d " ,cur);
    		}
    		for(int i = m / 2 ,cur ; i >= 1 ; --i){
    			if(i & 1) cur = orz[n - 1];
    			else cur = orz[0];
    			printf("%d " ,cur);
    		}
    		puts("");
    	}
    	else{
    		puts("NO");
    	}
    }
    
    int main(){
    	int t = read(); test = t;
    	while(t--) ++T ,solve();
    	return 0;
    }
    

    Editorial 2

    很容易想到,如果存在一条边,它与它的反边相同,那就一直走这两条就行了。

    很容易想到如果 (m) 为奇数,也可以搞。

    然后如果存在如下的一张子图,那也可以搞:

    1----->2
    |  a
    |
    |b
    V
    3
    

    即一个点同时有 (a)(b) 的出边。

    • (frac{m}{2}) 为奇数时,可以走:(21213131)
    • (frac{m}{2}) 为偶数时,可以走:(121213131)

    可以证明,如果排除掉之前的两种情况看,并且 (nge 3),那么图中一定会有这样的一个子图。

    于是就有了这份更简单的代码:

    #include <bits/stdc++.h>
    
    #define debug(...) fprintf(stderr ,__VA_ARGS__)
    #define __FILE(x)
    	freopen(#x".in" ,"r" ,stdin);
    	freopen(#x".out" ,"w" ,stdout)
    #define LL long long
    
    const int MX = 1000 + 233;
    const LL MOD = 998244353;
    
    int read(){
    	char k = getchar(); int x = 0;
    	while(k < '0' || k > '9') k = getchar();
    	while(k >= '0' && k <= '9') x = x * 10 + k - '0' ,k = getchar();
    	return x;
    }
    
    char s[MX][MX];
    void solve(){
    	int n = read() ,m = read();
    	for(int i = 1 ; i <= n ; ++i) scanf("%s" ,s[i] + 1);
    	for(int i = 1 ; i <= n ; ++i)
    		for(int j = i + 1 ; j <= n ; ++j)
    			if(s[i][j] == s[j][i] || (m & 1)){
    				puts("YES");
    				for(int k = 0 ; k <= m ; ++k) printf("%d%c" ,(k & 1 ? i : j) ," 
    "[k == m]);
    				return ;
    			}
    	if(n == 2){
    		puts("NO");
    		return ;
    	}
    	for(int i = 1 ; i <= n ; ++i){
    		int o[2] = {0 ,0};
    		for(int j = 1 ; j <= n ; ++j){
    			if(j == i) continue;
    			if(s[i][j] == 'a') o[0] = j;
    			else o[1] = j;
    		}
    		if(o[0] && o[1]){
    			puts("YES");
    			int rep = m / 2 ,tr = i ^ o[0] ,st = 0;
    			if(rep & 1) st = o[0];
    			else st = i;
    			for(int j = 0 ; j <= rep ; ++j){
    				printf("%d " ,st);
    				st ^= tr;
    			}
    			st ^= tr;
    			tr = i ^ o[1];
    			for(int j = 1 ; j <= rep ; ++j){
    				st ^= tr;
    				printf("%d%c" ,st ," 
    "[j == rep]);
    			}
    			return;
    		}
    	}
    	assert(0);
    }
    
    int main(){
    	int T = read();
    	for(int i = 1 ; i <= T ; ++i){
    		solve();
    	}
    	return 0;
    }
    
  • 相关阅读:
    Two Sum 2015年6月8日
    Word Ladder II 2015年6月4日
    Word Ladder 2015年6月3日
    华为
    《安卓网络编程》之第八篇 安卓与服务器之间通讯JSON
    星辉信息科技Odoo开发教程10-odoo开发环境准备
    星辉信息科技Odoo开发教程9-odoo创建视图02
    星辉信息科技Odoo开发教程8-odoo创建视图01
    星辉信息科技Odoo开发教程7-创建菜单项
    星辉信息科技Odoo开发教程6-配置安全权限控制02
  • 原文地址:https://www.cnblogs.com/imakf/p/14460710.html
Copyright © 2011-2022 走看看