2.1 基本计数方法
例题1 uva 11538 http://acm.hust.edu.cn/vjudge/problem/28978
在m*n棋盘上放置 黑白两个皇后,相互攻击的方法数.
解法,正着考虑, 同行,同列, 斜着的情况加在一起, 组合书计算一下, 黑白不同 所以是 P(n,2).
对斜着的角的情况暴力算也过. 1760ms
1 //#define txtout 2 //#define debug 3 #include<bits/stdc++.h> 4 #define mt(a,b) memset(a,b,sizeof(a)) 5 using namespace std; 6 typedef long long LL; 7 const double pi=acos(-1.0); 8 const double eps=1e-8; 9 const int inf=0x3f3f3f3f; 10 const int M=1e5+10; 11 int n,m; 12 LL getRow(int r,int c){ 13 return 1LL*c*(c-1)*r; 14 } 15 LL getFirst(int r){ 16 LL sum=0; 17 for(int i=2;i<=r;i++){ 18 sum+=1LL*i*(i-1); 19 } 20 return sum; 21 } 22 LL get(int r,int c){ 23 int small=min(r,c); 24 int sub=max(r,c)-small+1; 25 return getRow(sub,small)+2*getFirst(small-1); 26 } 27 LL solve(){ 28 return getRow(n,m)+getRow(m,n)+get(n,m)+get(m,n); 29 } 30 int main(){ 31 #ifdef txtout 32 freopen("in.txt","r",stdin); 33 freopen("out.txt","w",stdout); 34 #endif // txtout 35 while(~scanf("%d%d",&n,&m),n|m){ 36 printf("%lld\n",solve()); 37 } 38 return 0; 39 }
对斜着的角的预处理, 快非常多. 0ms
1 //#define txtout 2 //#define debug 3 #include<bits/stdc++.h> 4 #define mt(a,b) memset(a,b,sizeof(a)) 5 using namespace std; 6 typedef long long LL; 7 const double pi=acos(-1.0); 8 const double eps=1e-8; 9 const int inf=0x3f3f3f3f; 10 const int M=1e6+10; 11 int n,m; 12 LL sum[M]; 13 void init(){ 14 sum[0]=0; 15 sum[1]=0; 16 for(int i=2;i<M;i++){ 17 sum[i]=sum[i-1]+1LL*i*(i-1); 18 } 19 } 20 LL getRow(int r,int c){ 21 return 1LL*c*(c-1)*r; 22 } 23 LL getFirst(int r){ 24 return sum[r]; 25 } 26 LL get(int r,int c){ 27 int small=min(r,c); 28 int sub=max(r,c)-small+1; 29 return getRow(sub,small)+2*getFirst(small-1); 30 } 31 LL solve(){ 32 return getRow(n,m)+getRow(m,n)+get(n,m)+get(m,n); 33 } 34 int main(){ 35 #ifdef txtout 36 freopen("in.txt","r",stdin); 37 freopen("out.txt","w",stdout); 38 #endif // txtout 39 init(); 40 while(~scanf("%d%d",&n,&m),n|m){ 41 printf("%lld\n",solve()); 42 } 43 return 0; 44 }
例题2 uva 11401 http://acm.hust.edu.cn/vjudge/problem/22646
从1..n选3个不同整数, 作为三角形3边长, 求合法的方法数.
n3暴力,会超时, 通过for循环不断的化简,最后能o1算, 其中i^2的前n项和是用白书的公式, 推导可百度.
1 //#define txtout 2 //#define debug 3 #include<bits/stdc++.h> 4 #define mt(a,b) memset(a,b,sizeof(a)) 5 using namespace std; 6 typedef long long LL; 7 const double pi=acos(-1.0); 8 const double eps=1e-8; 9 const int inf=0x3f3f3f3f; 10 const int M=1e6+10; 11 LL first(LL x){ 12 return x*(x+1)*(2*x+1)/6; 13 } 14 LL n; 15 LL solve(){ 16 LL m=(n+1)/2; 17 LL sum=(n+3)*(m+1)*m/2-(n+1)*m-2*first(m); 18 sum+=first(m)/2+m-3*(1+m)*m/4; 19 if(m<=n-2){ 20 LL L=m+1; 21 LL R=n-1; 22 LL len=R-L+1; 23 LL t1=len*(n*n-n); 24 LL t2=len*(1-2*n)*(L+R)/2; 25 LL t3=first(R)-first(L-1); 26 sum+=(t1+t2+t3)/2; 27 } 28 return sum; 29 } 30 int main(){ 31 #ifdef txtout 32 freopen("in.txt","r",stdin); 33 freopen("out.txt","w",stdout); 34 #endif // txtout 35 // init(); 36 int x; 37 while(~scanf("%d",&x),x>=3){ 38 n=x; 39 printf("%lld\n",solve()); 40 } 41 return 0; 42 }
例题3 uva 11806
n*m的矩形格子里放k个相同石头, 保证第一行 ,最后一行, 第一列 最后一列都有石头的选法有多少种.
容斥原理, 总的情况数,扣去不在一个的情况, 加回来同时有两个不在的情况, 依次类推, 奇数个- 偶数个+
1 #include<bits/stdc++.h> 2 #define mt(a,b) memset(a,b,sizeof(a)) 3 using namespace std; 4 typedef long long LL; 5 const int M=1e3+10; 6 const LL MOD=1e6+7; 7 LL C[M][M]; 8 void init(){ 9 mt(C,0); 10 for(int i=0;i<M;i++){ 11 C[i][0]=1; 12 C[i][i]=1; 13 } 14 for(int i=1;i<M;i++){ 15 for(int j=1;j<i;j++){ 16 C[i][j]=C[i-1][j-1]+C[i-1][j]; 17 C[i][j]%=MOD; 18 } 19 } 20 } 21 int get_one(int x){ 22 int sum=0; 23 for(int i=0;i<4;i++){ 24 if((x>>i)&1) sum++; 25 } 26 return sum; 27 } 28 int n,m,k; 29 LL solve(){ 30 LL answer=0; 31 int total=1<<4; 32 for(int i=0;i<total;i++){ 33 int tn=n; 34 int tm=m; 35 for(int j=0;j<2;j++){ 36 if((i>>j)&1){ 37 tn--; 38 } 39 } 40 for(int j=2;j<4;j++){ 41 if((i>>j)&1){ 42 tm--; 43 } 44 } 45 LL value=C[tn*tm][k]; 46 int one=get_one(i); 47 if(one&1){ 48 answer=(answer+MOD-value)%MOD; 49 } 50 else{ 51 answer+=value; 52 answer%=MOD; 53 } 54 } 55 return answer; 56 } 57 int main(){ 58 init(); 59 int t; 60 while(~scanf("%d",&t)){ 61 int cas=1; 62 while(t--){ 63 scanf("%d%d%d",&n,&m,&k); 64 printf("Case %d: %lld\n",cas++,solve()); 65 } 66 } 67 return 0; 68 } 69 70 /** 71 72 2 73 2 2 1 74 2 3 2 75 76 */ 77 78 /** 79 80 Case 1: 0 81 Case 2: 2 82 */
end