Nebius Welcome Round (Div. 1 + Div. 2) E. Routing(状压dp/找到一个无向图简单环,使得所有点到环的距离不超过1)

题目

原题意比较难读,简单用自己的话翻译一下

给定一个n(n<=20)个点,m(n-1<=m<=C(n,2))条边,无自环、无重边、连通的无向图,

Ada从u找v的路径的流程,可以看做是以下这个函数

dfs(u):

1. 判断v是不是u的直连点,是的话,结束流程,返回u->v

2. 若第一步没找到,则从u走到a(u)上,再dfs(a[u])

若找到,函数返回u到v的路径,即dfs(u)=u->a(u)+dfs(a[u])

对于每个点u,构造a(u),要求u-a(u)的边在图上存在(有点竞赛图定向的意思)

使得从任意一个点u出发找v,都能找到,即函数不会死循环,输出a数组

思路来源

乱搞AC

题解

Codeforces Beta Round #11 D. A Simple Task(状压dp/无向图简单环的个数)_Code92007的博客-CSDN博客

可以发现,从任意一个点u出发,要找的点v,要么在u->a(u)的路径上,要么是a(u)的相邻点

这等价于,找到一个无向图简单环,使得所有点到环的距离不超过1,

可证充要性,如果存在v的距离>1则u找不到v,如果不存在的话所有点都能找到

让在环上的点形成一个环,不在环上的点往环上指即可

这里用到了完全图找环的技巧,即相同的环从环上最小点出发

原题中会减去环长为2的环,并答案除以2,因为顺逆时针各会统计一次,

本题中,环长为2的点也是答案,并且找到一个合法的环即可

做法是O(2^20*20*20)的,因为无自环重边,20*20实际是C(20,2)=190,2s可以通过

pre记录了一下转移前驱,can记录了一下当前状态dis<=1的可达点,是全集则表明合法

代码

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
typedef pair<int,int> P;
#define pb push_back
const int N=20,M=1<<20;
ll dp[M][N],can[M],ans;//dp[i][j]表示当前从lowbit(i)出发,访问了i内的点,且到达j的简单路径数
P pre[M][N];
int n,m,u,v,all,res[N];
vector<int>e[N];
int main(){
	memset(res,-1,sizeof res);
    scanf("%d%d",&n,&m);
    for(int i=0;i<n;++i){
    	can[1<<i]|=(1<<i);
    }
    for(int i=1;i<=m;++i){
        scanf("%d%d",&u,&v);
        u--;v--;
        can[1<<u]|=(1<<v);
        can[1<<v]|=(1<<u);
        e[u].pb(v);e[v].pb(u);
    }
    for(int i=0;i<n;++i){
        dp[1<<i][i]=1;
    }
    int up=1<<n;
    for(int i=1;i<up;++i){
    	for(int j=0;j<n;++j){
    		if(i>>j&1){
    			can[i]=can[i^(1<<j)]|can[1<<j];
    			break;
    		}
    	}
    }
    for(int i=1;i<up;++i){
        for(int j=0;j<n;++j){
            if(!dp[i][j])continue;
            for(int k=0;k<e[j].size();++k){
                int v=e[j][k];
                if((i&(-i))>(1<<v))continue;
                if(i&(1<<v)){
                    if((i&(-i))==(1<<v)){
                        if(can[i]==up-1){
                        	//printf("i:%d j:%d\n",i,j);
                        	puts("Yes");
                        	//printf("v:%d j:%d\n",v,j);
                        	res[v]=j;
                        	for(int x=i,y=j,px,py;x;x=px,y=py){
                        		px=pre[x][y].first,py=pre[x][y].second;
                        		if(!px)break;
                        		res[y]=py;
                        		//printf("x:%d y:%d px:%d py:%d\n",x,y,px,py);
                        	}
                        	for(int l=0;l<n;++l){
                        		if(res[l]==-1){
                        			for(auto &v:e[l]){
                        				if(i&(1<<v)){
                        					res[l]=v;
                        					break;
                        				}
                        			}
                        		}
                        	}
                        	for(int l=0;l<n;++l){
                        		printf("%d%c",res[l]+1," \n"[l==n-1]);
                        	}
                        	return 0;
                        }
                    }
                }
                else{
                    dp[i|(1<<v)][v]+=dp[i][j];
                    pre[i|(1<<v)][v]=P(i,j);
                }
            }
        }
    }
    puts("No");
    return 0;
}

猜你喜欢

转载自blog.csdn.net/Code92007/article/details/129484100