[CQOI2018]解锁屏幕

状压并不难,记录一下现在经过点的状态和最后一个经过的点即可。

但是有个限制条件是不能直接经过一个没经过的点。

如果直接做的话就时间复杂度2nn3,卡常+O2也过不了啊。。。Orz

所以预处理出来经过某一对点中间必然经过的一些点。

然后dp的时候就直接判断一下就行了。

时间复杂度可以降一个n然后就A了。。。

#include <iostream>
#include <cmath>
#include <algorithm>
#include <cstdio>
#include <cstring>
using namespace std;
short n;
const int mod=100000007;
struct Node {short x,y;}p[25];
int f[1<<20][21],ans,mst[21][21];
bool cmp(Node a,Node b) {return a.x==b.x?a.y<b.y:a.x<b.x;}
short ck(short a,short b,short c) {
    short xa=p[a].x,ya=p[a].y,xb=p[b].x,yb=p[b].y,xc=p[c].x,yc=p[c].y;
    if((max(xa,xb)>=xc&&min(xa,xb)<=xc&&max(ya,yb)>=yc&&min(ya,yb)<=yc))
        return (float)(xa-xb)/(ya-yb)==(float)(xa-xc)/(ya-yc);
    return 0;
}
int main() {
    cin>>n;
    for(short i=0;i<n;i++) cin>>p[i].x>>p[i].y;
    for(short i=0;i<n;i++) f[1<<i][i+1]=1;
    sort(p,p+n,cmp);
    for(short i=0;i<n;i++) {
        for(short j=0;j<n;j++) {
            if(i^j)
            for(short k=0;k<n;k++) 
            if((i^j)&&(i^k)&&(j^k)) mst[i][j]|=ck(i,j,k)<<k;
        }
    }
    register int i;register short j,k;
    for(i=1;i<(1<<n);i++) {
        for(j=0;j<n;j++) {
            if((1<<j)&i)
            for(k=0;k<n;k++) {
                if(!((1<<k)&i)) {
            if((i&mst[j][k])==mst[j][k]) {f[i|(1<<k)][k+1]+=f[i][j+1];if(f[i|(1<<k)][k+1]>=mod) f[i|(1<<k)][k+1]-=mod;}
                }
            }
        }
    }
    for(i=15;i<1<<n;i++) {
        if(__builtin_popcount(i)<4) continue;
        for(j=1;j<=n;j++) {if((1<<j-1)&i)ans=(ans+f[i][j]);if(ans>=mod) ans-=mod;}
    }
    cout<<ans<<endl;
    return 0;
}
解锁屏幕

 

posted @ 2018-10-12 08:23  SWHsz  阅读(186)  评论(0编辑  收藏  举报