BZOJ传送门
洛谷传送门
解析:
熟悉的感觉。。。洛谷RK1,BZOJ RK2,前面的又是工程大佬xehoth。。。
思路:
这道题真的卡常,本来说用BFS可以优化一些不可能达到的状态,从而减少计算量。。。我还是太naive了。。。结果复杂度算下来 O ( 2 n × n 2 ) O(2^n\times n^2) O(2n×n2)。。。
这个。。真的能过?
于是BFS卡常也只卡出了80pts。。
然后转手去写朴素的状压了。。。
发现很多地方可以优化,一不小心就过了。。。
其实主要就是通过逻辑判断将运算引向不同分支,从而减少计算量。
当然,合理安排内存分配位置也是卡常必要的技巧。
这道题的状压倒是非常好想,所有初始状态方案数设置为1,然后记录状态为 f j , s t a f_{j,sta} fj,sta,表示当前已经经过了sta的点,在 j j j点上的状态数。
转移就是直接枚举补集中的每个元素,判断能否直接转移到就行了。
注意我们不可以越过还没有经过的点。
最后,这道题卡常的关键在于:减少函数的调用。
因为函数的调用本身需要从内存中分配栈空间,略微有点耗时间。
对于一些函数值,可以预处理在数组里面就预处理在数组里面吧。
代码(BFS):
#include<bits/stdc++.h>
using namespace std;
#define ll long long
#define re register
#define gc get_char
#define pc put_char
#define cs const
namespace IO{
namespace IOONLY{
cs int Rlen=1<<18|1;
char buf[Rlen],*p1,*p2;
}
inline char get_char(){
using namespace IOONLY;
return (p1==p2)&&(p2=(p1=buf)+fread(buf,1,Rlen,stdin),p1==p2)?EOF:*p1++;
}
inline int getint(){
re int num;
re char c;
re bool f=0;
while(!isdigit(c=gc()))if(c=='-')f=1;num=c^48;
while(isdigit(c=gc()))num=(num+(num<<2)<<1)+(c^48);
return f?-num:num;
}
}
using namespace IO;
inline int max(cs int &x,cs int &y){return x>y?x:y;}
inline int min(cs int &x,cs int &y){return x<y?x:y;}
cs int mod=100000007;
inline void add(int &x,int y){
x+=y;
if(x>=mod)x-=mod;
}
cs int N=20;
struct state{
int now,sta;
state(){}
state(cs int &_now,cs int &_state):now(_now),sta(_state){}
};
queue<state> q;
int x[N],y[N];
int sta[N][N];
inline bool onseg(int i,int j,int k){
if(x[k]<min(x[i],x[j]) || x[k]>max(x[i],x[j]) || y[k]<min(y[i],y[j]) || y[k]>max(y[i],y[j]))return 0;
return ((x[k]-x[i])*(y[j]-y[k])-(x[j]-x[k])*(y[k]-y[i])==0);
}
inline bool enough(int sta){
return __builtin_popcount(sta)>=4;
}
inline bool check(int i,int j,int state){
return (sta[i][j]&state)==sta[i][j];
}
int f[N][1<<N];
bool inq[N][1<<N];
int n,ans;
signed main(){
n=getint();
for(int re i=0;i<n;++i){
x[i]=getint();
y[i]=getint();
}
for(int re i=0;i<n;++i){
for(int re j=0;j<n;++j)
if(i^j)for(int re k=0;k<n;++k){
if((i^k)&&(j^k)&&onseg(i,j,k))sta[i][j]|=1<<k;
}
}
for(int re i=0;i<n;++i)inq[i][1<<i]=true,f[i][1<<i]=1,q.push(state(i,1<<i));
while(!q.empty()){
re int u=q.front().now,sta=q.front().sta;q.pop();
if(enough(sta))add(ans,f[u][sta]);
for(int re i=0,j=1;i<n;++i,j<<=1)if(!(j&sta)){
if(!check(u,i,sta))continue;
if(!inq[i][sta|j])inq[i][sta|j]=true,q.push(state(i,sta|j));
add(f[i][sta|j],f[u][sta]);
}
}
printf("%d",ans);
return 0;
}
代码(朴素状压):
#include<bits/stdc++.h>
using namespace std;
#define ll long long
#define re register
#define gc get_char
#define pc putchar
#define cs const
namespace IO{
namespace IOONLY{
cs int Rlen=1<<15|1;
char buf[Rlen],*p1,*p2;
}
inline char get_char(){
using namespace IOONLY;
return (p1==p2)&&(p2=(p1=buf)+fread(buf,1,Rlen,stdin),p1==p2)?EOF:*p1++;
}
inline int getint(){
re int num;
re char c;
re bool f=0;
while(!isdigit(c=gc()))if(c=='-')f=1;num=c^48;
while(isdigit(c=gc()))num=(num+(num<<2)<<1)+(c^48);
return f?-num:num;
}
}
using namespace IO;
#define min(a,b) ((a)<(b)?(a):(b))
#define max(a,b) ((a)>(b)?(a):(b))
#define lowbit(x) ((x)&-(x))
cs int N=20;
cs int mod=100000007;
int f[1<<N][N],sig[1<<N];
int sum[1<<N];
int x[N],y[N],sta[N][N];
signed main(){
re int n=getint();
for(int re i=0;i<n;++i)x[i]=getint(),y[i]=getint(),sig[1<<i]=i,f[1<<i][i]=1;
for(int re i=0;i<n;++i){
for(int re j=0;j<n;++j)if(i^j){
re int dx=x[i]-x[j],dy=y[i]-y[j];
re int l=min(x[i],x[j]),r=max(x[i],x[j]);
re int u=max(y[i],y[j]),d=min(y[i],y[j]);
for(int re k=0;k<n;++k){
if(dx==0){
if(x[k]^x[i])continue;
if(y[k]<d||y[k]>u)continue;
sta[i][j]|=1<<k;
}
else {
if(x[k]<l||x[k]>r)continue;
if(dx*(y[k]-y[i])==dy*(x[k]-x[i]))sta[i][j]|=1<<k;
}
}
}
}
re int full=(1<<n)-1,ans=0;
for(int re i=1,j=1;i<=full;j=++i)
for(;j;j-=lowbit(j))++sum[i];
for(int re i=1;i<=full;++i){
for(int re j=0;j<n;++j){
if(f[i][j]){
f[i][j]%=mod;
if(sum[i]>3){
ans+=f[i][j];
if(ans>=mod)ans-=mod;
}
re int comps=full^i;
for(int re id=lowbit(comps);id;comps-=id,id=lowbit(comps)){
if((full^i^id)&sta[j][sig[id]])continue;
f[i|id][sig[id]]+=f[i][j];
}
}
}
}
printf("%d",ans);
return 0;
}