[关闭]
@ysner 2018-05-12T10:54:23.000000Z 字数 1522 阅读 1833

[CQOI2018]解锁屏幕

状压DP


题面

戳我

解析

这数据范围显然是状压。
可以发现,区分不同方案的标准是选的点终结点
于是设表示当选点状态为,最后选的点为时的方案数。
然后预处理一下连两点时中间经过的其它点这里注意判断条件问题,不能少等于号)。
再通过储存、延伸状态,统计时枚举下一个点,枚举中间点判是否路过未经过的点即可。
目测复杂度
但是我发现我傻逼了,好像就可以判不合法呢。
于是复杂度降到
然后因用被卡爆,被迫改成暴枚状态。。。

  1. #include<iostream>
  2. #include<cmath>
  3. #include<cstdio>
  4. #include<cstdlib>
  5. #include<cstring>
  6. #include<algorithm>
  7. #define mp make_pair
  8. #define ll long long
  9. #define re register
  10. #define il inline
  11. #define max(a,b) (a>b?a:b)
  12. #define min(a,b) (a<b?a:b)
  13. #define fp(i,a,b) for(re int i=a;i<=b-1;i++)
  14. #define fq(i,a,b) for(re int i=a;i>=b;i--)
  15. using namespace std;
  16. const int N=1<<20,mod=1e8+7;
  17. int n,way[30][30],x[30],y[30],dp[30][N],now,s;
  18. ll ans;
  19. il int gi()
  20. {
  21. re int x=0,t=1;
  22. re char ch=getchar();
  23. while(ch!='-'&&(ch<'0'||ch>'9')) ch=getchar();
  24. if(ch=='-') t=-1,ch=getchar();
  25. while(ch>='0'&&ch<='9') x=x*10+ch-48,ch=getchar();
  26. return x*t;
  27. }
  28. int main()
  29. {
  30. n=gi();
  31. fp(i,0,n) x[i]=gi(),y[i]=gi();
  32. fp(i,0,n)
  33. fp(j,0,n)
  34. fp(k,0,n)
  35. if(k!=i&&k!=j&&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]))
  36. if((y[k]-y[i])*(x[j]-x[i])==(y[j]-y[i])*(x[k]-x[i])) way[i][j]|=(1<<k);
  37. fp(i,0,n) dp[i][1<<i]=1;
  38. fp(now,0,(1<<n))
  39. fp(s,0,n)
  40. {
  41. if(!(now&(1<<s))||!dp[s][now]) continue;
  42. fp(i,0,n)
  43. {
  44. if((1<<i)&now) continue;
  45. if((way[s][i]&now)!=way[s][i]) continue;
  46. dp[i][now|(1<<i)]+=dp[s][now];
  47. if(dp[i][now|(1<<i)]>=mod) dp[i][now|(1<<i)]-=mod;
  48. }
  49. }
  50. fp(now,0,(1<<n))
  51. {
  52. re int tot=0,ysn=now;
  53. for(;ysn;ysn-=ysn&-ysn) ++tot;
  54. if(tot>3)
  55. fp(i,0,n) if((1<<i)&now)
  56. {
  57. ans+=dp[i][now];
  58. if(ans>=mod) ans-=mod;
  59. }
  60. }
  61. printf("%lld\n",ans);
  62. return 0;
  63. }
添加新批注
在作者公开此批注前,只有你和作者可见。
回复批注