Codeforces Beta Round #11 D. A Simple Task 题解

193 阅读1分钟

题目链接

思路

nn比较小,刚开始直接爆搜,从第ii个点出发,只去比当前点编号大的点,一旦回到起点,就维护一次答案。那么所有的方案数就是1n1\rightarrow n的全排列,复杂度达到O(n!)O(n!)级别。

正解是状压dp,设f[s][u]f[s][u]为经过点集ss中的所有点,到达uu点时的方案数(默认点集最右边的点为起点,显然可以用log2(lowbit(s))+1\log_2(lowbit(s))+1得到)。考虑一个与uu点相连的点vv,如果vv是起点,就可以直接用f[s][u]f[s][u]维护答案(也就是从vv出发走了一些路到达uu之后又回到了vv点),否则如果vv不在点集中,说明可以通过当前这条路到达vv,将f[s][u]f[s][u]维护进f[sv][v]f[s\cup v][v]中。

但是这样会有重复计算,比如长度只有22的环也会被维护进答案,总数有mm个(每个单独的边都会被算作一个环)。另外,每个长度大于22的环都会被记录两遍(顺时针和逆时针),最后要除以22

代码

#include<bits/stdc++.h>
#define rep(i,st,ed) for(int i=st;i<=ed;++i)
#define bl(u,i) for(int i=head[u];i;i=e[i].nxt)
#define LLM LONG_LONG_MAX
#define LLm LONG_LONG_MIN
#define pii pair<ll,ll> 
typedef long long ll;
typedef double db;
using namespace std;
const ll INF=0x3f3f3f3f;
void read() {}
void say() {}
void say_() {}
template <typename T, typename... T2>
inline void read(T &_, T2 &... oth)
{
    int __=0;
    _=0;
    char ch=getchar();
    while(!isdigit(ch))
    {
        if(ch=='-')
            __=1;
        ch=getchar();
    }
    while(isdigit(ch))
    {
        _=_*10+ch-48;
        ch=getchar();
    }
    _=__?-_:_;
    read(oth...);
}
template <typename T>
void Out(T _)
{
    if(_<0)
    {
        putchar('-');
        _=-_;
    }
    if(_>=10)
       Out(_/10);
    putchar(_%10+'0');
}
template <typename T, typename... T2>
inline void say(T _, T2... oth)
{
	Out(_);
	putchar('\n');
	say(oth...);
}
template <typename T, typename... T2>
inline void say_(T _, T2... oth)
{
	Out(_);
	putchar(' ');
	say_(oth...);
}
/*#################################*/
const ll N=19,M=405;
ll n,m,tot,ans;
ll f[1ll<<N|1][N],G[N][N];
int main()
{
	read(n,m);
	ll u,v;
	rep(i,1,m)
	{
		read(u,v);
		G[u][v]=G[v][u]=1;
	}
	rep(i,0,n-1)
		f[1ll<<i][i+1]=1;
	ll lim=(1ll<<n)-1;
	rep(i,1,lim)
	{
		rep(u,1,n)
		{
			if(!f[i][u])
				continue;
			ll lb=ll(log2(i&-i))+1;
			rep(v,lb,n)
			{
				if(u!=v && G[u][v])
				{
					if(v==lb)
						ans+=f[i][u];
					else if(!(i&(1ll<<(v-1))))
						f[i|(1ll<<(v-1))][v]+=f[i][u];
				}
			}
		}
	}
	ans-=m;
	ans>>=1;
	say(ans);
}