网站:CSUST7月23号
A题:大意是:一个N多边形,用红,绿,蓝三色给定点上色,要求划分成顶点颜色不同的三角形。
解析:
这道题是黑书上分治法的例题,还是比较巧的。
首先很容易发现当某种颜色的点只有一个时,很容易解决,只需将该点与其余的点连起来即可。
当每种颜色的点都超过一个时,可以证明必定有三个不同颜色的点连在一起,将这三个点连成一个三角形即可。
于是就可以不断减少点的个数,转化为某种颜色只有一个点的情况。
1 #include<iostream> 2 #include<stdio.h> 3 #include<string.h> 4 using namespace std; 5 int r,g,b,c[10010],l; 6 int f(char a[]) 7 { 8 int i,j; 9 r=b=g=0; 10 for(i=0;i<l;i++) //每次都要统计颜色的个数 11 { 12 if(a[i]=='R') 13 r++; 14 else if(a[i]=='G') 15 g++; 16 else if(a[i]=='B') 17 b++; 18 } 19 if(r==1) 20 { 21 for(i=0;i<l;i++) 22 if(a[i]=='R') 23 { 24 for(j=i-2;j>=(i==l-1?1:0);j--) 25 printf("%d %d\n",c[i]+1,c[j]+1); 26 for(j=i+2;j<(i==0?l-1:l);j++) 27 printf("%d %d\n",c[i]+1,c[j]+1); 28 } 29 return 0; 30 } 31 else if(g==1) 32 { 33 for(i=0;i<l;i++) 34 if(a[i]=='G') 35 { 36 for(j=i-2;j>=(i==l-1?1:0);j--) 37 printf("%d %d\n",c[i]+1,c[j]+1); 38 for(j=i+2;j<(i==0?l-1:l);j++) 39 printf("%d %d\n",c[i]+1,c[j]+1,l); 40 } 41 return 0; 42 } 43 else if(b==1) 44 { 45 for(i=0;i<l;i++) 46 if(a[i]=='B') 47 { 48 49 for(j=i-2;j>=(i==l-1?1:0);j--) 50 printf("%d %d\n",c[i]+1,c[j]+1); 51 for(j=i+2;j<(i==0?l-1:l);j++) 52 printf("%d %d\n",c[i]+1,c[j]+1); 53 } 54 return 0; 55 } 56 else 57 { 58 for(i=0;i<l-3;i++) 59 if(a[i]!=a[i+1]&&a[i+1]!=a[i+2]&&a[i]!=a[i+2]) 60 { 61 printf("%d %d\n",c[i]+1,c[i+2]+1); 62 for(j=i+1;j<l-1;j++) //删去中间那个点 63 { 64 a[j]=a[j+1]; 65 c[j]=c[j+1]; 66 } 67 c[l-1]='\0'; 68 l--; //总数-1 69 break; 70 } 71 } 72 if(f(a)) 73 return 0; 74 return 0; 75 } 76 int main() 77 { 78 int n,i; 79 char a[10010]; 80 scanf("%d",&n); 81 scanf("%s",a); 82 l=n; 83 if(a[0]==a[n-1]) //两个相邻的点同色无解 84 { 85 printf("0\n"); 86 return 0; 87 } 88 for(i=0;i<n-1;i++) //两个相邻的点同色无解 89 if(a[i]==a[i+1]) 90 { 91 printf("0\n"); 92 break; 93 } 94 r=g=b=0; 95 for(i=0;i<n;i++) //统计 96 { 97 if(a[i]=='R') 98 r++; 99 else if(a[i]=='G') 100 g++; 101 else if(a[i]=='B') 102 b++; 103 } 104 if(r==0||b==0||g==0) //少一个色即无解 105 { 106 printf("0\n"); 107 return 0; 108 } 109 printf("%d\n",n-3); 110 for(i=0;i<n;i++) 111 c[i]=i; // 标记原来的编号 112 f(a); 113 return 0; 114 }
本来在子函数里,长度我是每次都重新统计一次的,但是最后l还是等于最初的那个长度,所以我干脆来个全局变量,再每去掉一个点的时候l--,答案才终于对了......我的答案和测试的答案不同,反正是有多种可能,选一种即可。
B:有一个递归函数,但是直接用递归果断超时,请把它写得不超时......Orz,目前有两种方法:1,记忆法,2,找规律,然后再递归。
1 #include<stdio.h> 2 #include<string.h> 3 #include<math.h> 4 using namespace std; 5 int x[22][22][22]; 6 int w(int i,int j,int k) 7 { 8 if(i<=0 || j<=0 ||k<=0) 9 return 1; 10 else 11 return x[i][j][k]; 12 } 13 void zhunbei() //准备函数,把这些数都准备好,直接调用就行 14 { 15 int i,j,k; 16 for(i=1;i<21;i++) 17 for(j=1;j<21;j++) 18 for(k=1;k<21;k++) 19 { 20 if(i <= j&&j<=k) 21 x[i][j][k]=pow(2,i); //这个找规律找粗来的= = 22 else 23 x[i][j][k]=w(i-1,j,k)+w(i-1,j-1,k)+w(i-1,j,k-1)-w(i-1,j-1,k-1); 24 } 25 } 26 int main() 27 { 28 int a,b,c; 29 while(~scanf("%d%d%d",&a,&b,&c)) 30 { 31 if(a==-1 && b==-1 && c==-1) 32 break; 33 if(a<=0 ||b<=0 || c<=0) 34 printf("w(%d, %d, %d) = 1\n",a,b,c); 35 else if(a>20 || b>20 || c>20) 36 printf("w(%d, %d, %d) = 1048576\n",a,b,c); //因为只要A,B,C中有一个>20就等于w(20,20,20),即1048576 37 else 38 { 39 zhunbei(); 40 printf("w(%d, %d, %d) = %d\n",a,b,c,x[a][b][c]); 41 } 42 } 43 return 0; 44 }
做的时候,在zhunbei()上死了一小时,一直死循环......其实可先把x[i][j][k]初始化为1,代码会更简单。
C 大意是输入A,B,求A^B的所有因子之和,对9901取模,如:2^3=8,sum=1+2+4+8=15,输出15.
本来的想法是先求出A的所有因数,在相乘,求和,但是后来找不到方法去实现╮(╯▽╰)╭
下面是大神的解释&代码,出处:http://blog.csdn.net/lyy289065406/article/details/6648539
解题思路:
要求有较强 数学思维 的题
应用定理主要有三个:
要求有较强 数学思维 的题
应用定理主要有三个:
(1) 整数的唯一分解定理:
任意正整数都有且只有一种方式写出其素因子的乘积表达式。
A=(p1^k1)*(p2^k2)*(p3^k3)*....*(pn^kn) 其中pi均为素数
(2) 约数和公式:
对于已经分解的整数A=(p1^k1)*(p2^k2)*(p3^k3)*....*(pn^kn)
有A的所有因子之和为
S = (1+p1+p1^2+p1^3+...p1^k1) * (1+p2+p2^2+p2^3+….p2^k2) * (1+p3+ p3^3+…+ p3^k3) * .... * (1+pn+pn^2+pn^3+...pn^kn)
(3) 同余模公式:
(a+b)%m=(a%m+b%m)%m
(a*b)%m=(a%m*b%m)%m
有了上面的数学基础,那么本题解法就很简单了:
1: 对A进行素因子分解
分解A的方法:
A首先对第一个素数2不断取模,A%2==0时 ,记录2出现的次数+1,A/=2;
当A%2!=0时,则A对下一个连续素数3不断取模...
以此类推,直到A==1为止。
注意特殊判定,当A本身就是素数时,无法分解,它自己就是其本身的素数分解式。
最后得到A = p1^k1 * p2^k2 * p3^k3 *...* pn^kn. 故 A^B = p1^(k1*B) * p2^(k2*B) *...* pn^(kn*B);
2:A^B的所有约数之和为:
sum = [1+p1+p1^2+...+p1^(a1*B)] * [1+p2+p2^2+...+p2^(a2*B)] *...* [1+pn+pn^2+...+pn^(an*B)].
3: 用递归二分求等比数列1+pi+pi^2+pi^3+...+pi^n:
(1)若n为奇数,一共有偶数项,则: 1 + p + p^2 + p^3 +...+ p^n
= (1+p^(n/2+1)) + p * (1+p^(n/2+1)) +...+ p^(n/2) * (1+p^(n/2+1)) = (1 + p + p^2 +...+ p^(n/2)) * (1 + p^(n/2+1))
上式红色加粗的前半部分恰好就是原式的一半,那么只需要不断递归二分求和就可以了,后半部分为幂次式,将在下面第4点讲述计算方法。
(2)若n为偶数,一共有奇数项,则: 1 + p + p^2 + p^3 +...+ p^n
= (1+p^(n/2+1)) + p * (1+p^(n/2+1)) +...+ p^(n/2-1) * (1+p^(n/2+1)) + p^(n/2) = (1 + p + p^2 +...+ p^(n/2-1)) * (1+p^(n/2+1)) + p^(n/2);
上式红色加粗的前半部分恰好就是原式的一半,依然递归求解
4:反复平方法计算幂次式p^n
这是本题关键所在,求n次幂方法的好坏,决定了本题是否TLE。
以p=2,n=8为例
常规是通过连乘法求幂,即2^8=2*2*2*2*2*2*2*2
这样做的要做8次乘法
而反复平方法则不同,
定义幂sq=1,再检查n是否大于0,
While,循环过程若发现n为奇数,则把此时的p值乘到sq
{
n=8>0 ,把p自乘一次, p=p*p=4 ,n取半 n=4
n=4>0 ,再把p自乘一次, p=p*p=16 ,n取半 n=2
n=2>0 ,再把p自乘一次, p=p*p=256 ,n取半 n=1,sq=sq*p
n=1>0 ,再把p自乘一次, p=p*p=256^2 ,n取半 n=0,弹出循环
}
则sq=256就是所求,显然反复平方法只做了3次乘法
代码:
1 #include<iostream> 2 using namespace std; 3 const int size=10000; 4 const int mod=9901; 5 __int64 sum(__int64 p,__int64 n); //递归二分求 (1 + p + p^2 + p^3 +...+ p^n)%mod 6 __int64 power(__int64 p,__int64 n); //反复平方法求 (p^n)%mod 7 8 int main(void) 9 { 10 int A,B; 11 int p[size];//A的分解式,p[i]^n[i] 12 int n[size]; 13 14 while(cin>>A>>B) 15 { 16 int i,k=0; //p,n指针 17 18 /*常规做法:分解整数A (A为非质数)*/ 19 for(i=2;i*i<=A;) //根号法+递归法 20 { 21 if(A%i==0) 22 { 23 p[k]=i; 24 n[k]=0; 25 while(!(A%i)) 26 { 27 n[k]++; 28 A/=i; 29 } 30 k++; 31 } 32 if(i==2) //奇偶法 33 i++; 34 else 35 i+=2; 36 } 37 /*特殊判定:分解整数A (A为质数)*/ 38 if(A!=1) 39 { 40 p[k]=A; 41 n[k++]=1; 42 } 43 44 int ans=1; //约数和 45 for(i=0;i<k;i++) 46 ans=(ans*(sum(p[i],n[i]*B)%mod))%mod; //n[i]*B可能会超过int,因此用__int64 47 48 cout<<ans<<endl; 49 } 50 return 0; 51 } 52 53 __int64 sum(__int64 p,__int64 n) //递归二分求 (1 + p + p^2 + p^3 +...+ p^n)%mod 54 { //奇数二分式 (1 + p + p^2 +...+ p^(n/2)) * (1 + p^(n/2+1)) 55 if(n==0) //偶数二分式 (1 + p + p^2 +...+ p^(n/2-1)) * (1+p^(n/2+1)) + p^(n/2) 56 return 1; 57 if(n%2) //n为奇数, 58 return (sum(p,n/2)*(1+power(p,n/2+1)))%mod; 59 else //n为偶数 60 return (sum(p,n/2-1)*(1+power(p,n/2+1))+power(p,n/2))%mod; 61 } 62 63 __int64 power(__int64 p,__int64 n) //反复平方法求(p^n)%mod 64 { 65 __int64 sq=1; 66 while(n>0) 67 { 68 if(n%2) 69 sq=(sq*p)%mod; 70 n/=2; 71 p=p*p%mod; 72 } 73 return sq; 74 }
我只是好奇,为什么不直接用pow(n,i),貌似pow()里不可以用_int64,而用等比数列的求和公式会超_int64的范围。
D 两个坐标集,求两个坐标集之间的最短距离。
另开一篇随笔吧......http://www.cnblogs.com/riddle/p/3220110.html
E ,博弈问题,详情见:http://www.cnblogs.com/riddle/p/3218101.html (另一篇随笔)
F.....
G: 给出一个数,你要找出一个比这个数大,而且,你要找的这个数的各个位之和要是10的倍数,而且,要最小。注意所给的数上午长度<100000,长度!!!!!故要用字符串做。
1 #include<iostream> 2 #include<stdio.h> 3 #include<string.h> 4 using namespace std; 5 int main() 6 { 7 int n,i,j,sum,l,a[200500]; 8 char b[200500]; 9 scanf("%d",&n); 10 while(n--) 11 { 12 scanf("%s",b); 13 l=strlen(b); //长度 14 for(i=1;i<=l;i++) 15 a[i]=b[l-i]-'0'; //个位到a[1],最高位到a[n] 16 for(;;) 17 { 18 sum=0; 19 a[1]++; 20 i=1; 21 while(a[i]==10) //满10 高位进1 22 { 23 if(i==l) //当最高位满10时,加一位 24 { 25 l++; 26 a[l]=0; 27 } 28 a[i]=0; 29 a[++i]++; 30 } 31 for(j=1;j<=l;j++) //每加1就算一次和 32 sum+=a[j]; 33 if(sum%10==0) 34 { 35 for(j=l;j>=1;j--) 36 printf("%d",a[j]); 37 printf("\n"); 38 break; 39 } 40 } 41 } 42 return 0; 43 }
这道题特别要注意进位。 时间1250MS....还好限制是5000ms......要不然就挂了T^T