集合选数
大概就是让你找方案数嘛。开始我还以为是一道规律题,然后有愉快地打了一个暴搜打表。
找了十分钟没找出来。www.oeis.org。结果... ...
半点规律没有。然后想了想DP,想不出线性或带log的。
最后实在做不出来了,于是问了个大犇。大犇说这道题要用矩形。尼玛没在逗我。
把题目意思转换一下嘛。就是有了X,就不能有2X和3X。
然后依大犇所言构造一个介样的矩形:
1 | 3 | 9 | 27 |
2 | 6 | 18 | 54 |
4 | 12 | 36 | ... |
8 | 24 | 72 | ... |
16 | 48 | ... | ... |
这样的话就构造出一个矩阵,满足 A[I][J]=2*A[I-1][J]=3*A[I][J-1];
然后你就会发现,对于一个合法的方案,你选择了一个点,它的上下左右的点都不能选。
然后想起炮兵阵地这道题,状压DP即可求出解(代码甚至更简单)。
注意到这个矩阵内没有5,7,10... ... ,即以它们为左上角的点的方案没有算进去,于是开vis数组。因为各个矩阵内的数字互不影响(因为它们的基数不同),所以用乘法原理。
代码什么的看看就好了,毕竟这题思维偏重。
1 #include <iostream> 2 #include <cstdio> 3 #include <cstdlib> 4 #include <algorithm> 5 #include <vector> 6 #include <cstring> 7 #define LL long long int 8 #define ls (x << 1) 9 #define rs (x << 1 | 1) 10 #define fa (x >> 1) 11 #define MID int mid=(l+r)>>1 12 #define max(a,b) (a>b?a:b) 13 #define min(a,b) (a<b?a:b) 14 #define MOD 1000000001 15 using namespace std; 16 LL n,vis[101010],f[110][2049],a[21][21],Ans=1,line[40],tmp; 17 LL gi() 18 { 19 LL x=0,res=1;char ch=getchar(); 20 while(ch!='-' && (ch>'9' || ch<'0'))ch=getchar(); 21 if(ch=='-')res=-1,ch=getchar(); 22 while(ch<='9'&&ch>='0')x=(x<<3)+(x<<1)+ch-48,ch=getchar(); 23 return x*res; 24 } 25 LL calc(LL x) 26 { 27 a[1][1]=x; 28 for(int i=1;;++i) 29 { 30 if(i!=1) 31 { 32 a[i][1]=a[i-1][1]*2; 33 if(a[i][1]>n) 34 {tmp=i-1;break;} 35 } 36 vis[a[i][1]]=1; 37 for(int j=2;;++j) 38 { 39 a[i][j]=a[i][j-1]*3; 40 if(a[i][j]>n) 41 {line[i]=j-1;break;} 42 vis[a[i][j]]=1; 43 } 44 } 45 line[0]=1; 46 for(int i=0;i<=tmp;++i) 47 for(int j=0;j<(1<<line[i]);++j) 48 f[i][j]=0; 49 line[tmp+1]=0;f[tmp+1][0]=0;f[0][0]=1; 50 for(int i=0;i<=tmp;++i) 51 { 52 for(int j=0,k1=1<<line[i];j<k1;++j) 53 { 54 if(f[i][j]) 55 { 56 if(j & j>>1)continue; 57 for(int k=0,k2=1<<line[i+1];k<k2;++k) 58 { 59 if(j&k)continue; 60 f[i+1][k]=(f[i+1][k]+f[i][j])%MOD; 61 } 62 } 63 } 64 } 65 return f[tmp+1][0]; 66 } 67 int main() 68 { 69 n=gi(); 70 for(LL i=1;i<=n;++i) 71 if(!vis[i]) 72 Ans=(Ans*calc(i))%MOD; 73 cout<<Ans; 74 }