bzoj 5299: [Cqoi2018]解锁屏幕 状压dp+二进制

时间:2023-03-08 16:13:42

比较简单的状压 dp,令 $f[S][i]$ 表示已经经过的点集为 $S$,且最后一个访问的位置为 $i$ 的方案数.

然后随便转移一下就可以了,可以用 $lowbit$ 来优化一下枚举.

code:

#include <bits/stdc++.h>
#define N 21
#define LL long long
#define setIO(s) freopen(s".in","r",stdin)
using namespace std;
const LL mod=100000007;
const double eps=1e-4,inf=100000000.0;
int tmp[N],v[N][N],vis[N],Log[1<<N];
LL f[1<<N][N];
int lowbit(int t)
{
return t&(-t);
}
struct data
{
double x,y;
data(double x=0,double y=0):x(x),y(y){}
}a[N];
double slope(int x,int y)
{
if(abs(a[x].x-a[y].x)<=eps) return inf;
else return (a[x].y-a[y].y)/(a[x].x-a[y].x);
}
int main()
{
// setIO("input");
int i,j,n,k;
scanf("%d",&n);
Log[1]=0;
for(i=2;i<(1<<N);++i) Log[i]=Log[i>>1]+1;
for(i=0;i<=n;++i) tmp[i]=1<<i;
for(i=0;i<n;++i) scanf("%lf%lf",&a[i].x,&a[i].y);
for(i=0;i<n;++i) f[tmp[i]][i]=1ll;
for(i=0;i<n;++i)
{
for(j=0;j<n;++j)
{
for(k=0;k<n;++k)
{
if(k!=i&&k!=j)
{
double slope1=slope(k,i),slope2=slope(k,j);
if(abs(slope1-slope2)<=eps&&a[k].x>=min(a[i].x,a[j].x)&&a[k].x<=max(a[i].x,a[j].x)&&a[k].y>=min(a[i].y,a[j].y)&&a[k].y<=max(a[i].y,a[j].y))
{
v[i][j]|=tmp[k];
}
}
}
}
}
int S;
LL ans=0ll;
for(S=0;S<(1<<n);++S)
{
for(k=0;k<n;++k)
{
int ss=S;
while(ss)
{
j=Log[lowbit(ss)];
if(!(S&(tmp[k]))&&(v[j][k]&S)==v[j][k]) (f[S|tmp[k]][k]+=f[S][j])%=mod;
ss-=lowbit(ss);
}
}
}
for(S=0;S<(1<<n);++S)
{
int sz=0;
for(j=0;j<n;++j) if(S&tmp[j]) ++sz;
if(sz>=4)
{
for(j=0;j<n;++j) if(S&tmp[j]) (ans+=f[S][j])%=mod;
}
}
printf("%lld\n",ans);
return 0;
}