T1 「SDOI2015」排序
Loj上的一道暴搜题,挺水的(但是考试的时候还是只拿了10分,还是水的)
考试的时候没有好好的想一想,其实再想想,在努努力打个大暴搜,没准儿能A。
简单推导可以发现,只要这个数列可以通过某种方式回到合法状态,那么无论操作的顺序如何,结果都是一样的。那么就显然了,假设操作了n次,那答案就是n的阶乘。(考试的时候也仅仅想到了这儿,加了个特判。)
对于每一组数列,我们可以找到不止一种(指的是一个操作系列)操作来还原它,比如
1 10 11 12 13 14 15 16 9 2 3 4 5 6 7 8
在该数组中,可以先将1与9换,也可以将10与2换,同样可以还原回去。
发现了这一点,就可以开始暴搜了。
设cz(操作)初始为1,一共进行了num次操作,逐层往下递归,每次cz*2,直到操作达到最大即2n,这时搜一遍串判断该串是否合法,不合法返回,反之求num的阶乘。
注意各个情况要考虑到,换对象能各种换,2×2=4。
结束了。
小弟不才。
1 #include<cstdio> 2 #include<cstring> 3 #include<iostream> 4 #define Mojie using namespace std 5 Mojie; 6 int n,m; 7 int a[1<<13]; 8 bool flag[1<<13]; 9 int ans; 10 void Dfs(int ,int ); 11 void Swap(int ,int ,int ); 12 int main() 13 { 14 scanf("%d",&n); 15 m=(1<<n); 16 for (int i=1; i<=m; ++i) 17 {scanf("%d",&a[i]);} 18 Dfs(1,0); 19 printf("%d ",ans); 20 } 21 void Dfs(int cz,int num) 22 { 23 if (cz==m) 24 { 25 for (int i=1; i<=m; ++i) 26 if (a[i]!=i) 27 return ; 28 int res=1; 29 for (int i=2; i<=num; ++i) 30 res*=i; 31 ans+=res; 32 return ; 33 } 34 int lh=cz<<1,tt=0; 35 memset(flag,0,sizeof(flag)); 36 for (int i=1; i<=m; i+=lh) 37 { 38 if (a[i]+cz==a[i+cz]) flag[i]=1; 39 else ++tt; 40 } 41 if (tt>2) return; 42 else if (tt==0) Dfs(cz<<1,num); 43 else if (tt==1) 44 { 45 int y,t=0; 46 for (int i=1; i<=m; i+=lh) 47 if (!flag[i]) 48 {y=i; break;} 49 Swap(y,y+cz,cz); 50 Dfs(cz<<1,num+1); 51 Swap(y,y+cz,cz); 52 } 53 else 54 { 55 int x[2],t=0; 56 for (int i=1; i<=m; i+=lh) 57 if (!flag[i]) 58 x[t++]=i; 59 Swap(x[0],x[1],cz); 60 Dfs(cz<<1,num+1); 61 Swap(x[0],x[1],cz); 62 Swap(x[0]+cz,x[1]+cz,cz); 63 Dfs(cz<<1,num+1); 64 Swap(x[0]+cz,x[1]+cz,cz); 65 Swap(x[0],x[1]+cz,cz); 66 Dfs(cz<<1,num+1); 67 Swap(x[0],x[1]+cz,cz); 68 Swap(x[0]+cz,x[1],cz); 69 Dfs(cz<<1,num+1); 70 Swap(x[0]+cz,x[1],cz); 71 } 72 } 73 void Swap(int l,int r,int step) 74 { 75 for (int i=l,j=r; i<l+step,j<r+step; ++i,++j) 76 swap(a[i],a[j]); 77 }
T3 【CQOI2011】放棋子
暴搜是不会实现的了,考虑dp。
定义dp[i][j][k]为在任意i行任意j列放置前k中棋子的方案数,g[i][j][k]为在任意i行任意j列放k个同种棋子的方案数。
dp[i][j][k]=Σdp[l][r][k-1]*g[i-l][j-r][a[k]]*C(n-l,i-l)*C(m-r,j-r) (l和r取不到i和j)
g[i][j][k]=C(k,i*j)-Σg[l][r][k]*C(l,i)*C(r,j) (注意理解一下这里,l,r>=0且l<=i||r<=j)
求g的加和不好求,用到了容斥。
注意dp定义,任意i行j列。
还有一点就是取模的问题,对于1e9+9的模数,如果不及时取模就会爆long long,在算g时还会算成负数。
小弟不才。
1 #include<cstdio> 2 #include<iostream> 3 #define Ojbk using namespace std 4 Ojbk; 5 const long long mod=1e9+9; 6 int n,m,c,maxx; 7 long long F[10003],Finv[10003],inv[10003]; 8 int a[11]; 9 long long f[33][33][33],g[33][33][333]; 10 void Init(); 11 inline long long Comb(int ,int ); 12 int main() 13 { 14 scanf("%d%d%d",&n,&m,&c); 15 Init(); 16 for (int i=1; i<=c; ++i) 17 {scanf("%d",&a[i]); maxx=max(a[i],maxx);} 18 for (int k=1; k<=c; ++k) 19 for (int i=1; i<=n; ++i) 20 for (int j=1; j<=m; ++j) 21 { 22 g[i][j][a[k]]=Comb(i*j,a[k]); 23 for (int l=1; l<=i; ++l) 24 for (int r=1; r<=j; ++r) 25 { 26 if (i==l and j==r) break; 27 g[i][j][a[k]]-=g[l][r][a[k]]*1ll%mod*Comb(i,l)%mod*Comb(j,r)%mod; 28 g[i][j][a[k]]=(g[i][j][a[k]]%mod+mod)%mod; 29 } 30 } 31 f[0][0][0]=1; 32 for (int k=1; k<=c; ++k) 33 for (int i=1; i<=n; ++i) 34 for (int j=1; j<=m; ++j) 35 for (int l=0; l<i; ++l) 36 for (int r=0; r<j; ++r) 37 { 38 f[i][j][k]+=f[l][r][k-1]%mod*g[i-l][j-r][a[k]]%mod*1ll*Comb(n-l,i-l)%mod*Comb(m-r,j-r)%mod; 39 f[i][j][k]=(f[i][j][k]%mod+mod)%mod; 40 } 41 long long ans=0; 42 for (int i=1; i<=n; ++i) 43 for (int j=1; j<=m; ++j) 44 {ans+=f[i][j][c]; ans=(ans%mod+mod)%mod;} 45 printf("%lld ",ans); 46 return 0; 47 } 48 inline long long Comb(int x,int y) 49 { 50 if (y>x) return 0; 51 return F[x]%mod*1ll*Finv[y]%mod*Finv[x-y]%mod; 52 } 53 void Init() 54 { 55 inv[1]=1; 56 for (int i=2; i<=10000; ++i) 57 inv[i]=(mod-mod/i)*1ll*inv[mod%i]%mod; 58 F[0]=Finv[0]=1; 59 for (int i=1; i<=10000; ++i) 60 { 61 F[i]=F[i-1]*1ll*i%mod; 62 Finv[i]=Finv[i-1]%mod*1ll*inv[i]%mod; 63 } 64 }
永不放弃