1008
1108
1061
题目让求N^N的最低位,N的最低位只与它最低位的N次方有关系,所以我们对一个数求它的N次方的时候,只考虑最后一位的连乘。
一个数连乘是有规律的,比如2,循环节就是2,4,8,6。数组result[]保存得就是我们的循环节。
源码如下:
#include <iostream> using namespace std; const int N=10; bool used[N]; int result[N]; int main() { int t,n,i; cin>>t; while(t-->0) { cin>>n; int dig=n%10; int tmp=dig; i=0; memset(used,0,sizeof(used)); while(!used[tmp]) { used[tmp]=1; result[i++]=tmp; tmp=(tmp*dig)%10; } int rs=result[(n-1)%i]; cout<<rs<<endl; } return 0; }
2035
求A^B的最后三位
2 3 输出8,12 6 输出984
利用性质(a*b)%m=(a%m*b)%m。
源码:
#include <iostream> using namespace std; int main() { int a,b,ans; while(cin>>a>>b) { if(!a && !b) break; ans=0; a=a%1000; int tmp=a; while(b-->1) { a=(a*tmp)%1000; } cout<<a<<endl; } return 0; }
1425
1021
f(1)=7,f(2)=11,f(n)=f(n-1)+f(n-2),输入一个n,判断是否能被3整除
找规律,对3取余只有3中输出0,1,2,那么f(n-2)f(n-1)就由3*3个组合情况,那么经过9个数
就会出现循环,可以列出来前几个mod3的值:
1 2 0 2 2 1 0 1 1 2 0 。。。
1005
已知f1=1,f2=1,fn=(a*fn-1+b*fn-2)%7,题目给出a,b,n求出fn
由于求对7取模,那么肯定会出现循环节(求mod运算大都会由这个规律),如果直接暴力求,肯定TLE,因为这里n为 100,000,000。
由于所有的状态组合为7*7种,所以我们只需要开一个50的数组记录状态就可以了,但是这个题出的ms有问题,所以开大点,
如果出现f[cnt-1]==1 ,f[cnt]==1,则回到了初始状态了,这时候就求出来循环节了。这里循环节长度为cnt-2,我们利用cnt%cnt得到对应下标,如果被cnt
整除,则结果为result[cnt](末尾元素).
源码:
#include <iostream> #include <stdio.h> using namespace std; const int N=200; bool used[7][7]; int result[N]; int main() { int n,a,b; while(cin>>a>>b>>n) { if(!(a+b+n)) break; memset(used,0,sizeof(used)); used[1][1]=true; int cnt=3; result[1]=1; result[2]=1; for(cnt=3;cnt<200;++cnt) { result[cnt]=(result[cnt-1]*a+result[cnt-2]*b)%7; if((1 == result[cnt-1]) && (1 == result[cnt])) break; } /* 考虑循环节不一定就是出现在1 1 ,可能为1 1 3 4 6 2 。。 3 4 。。。,所以定义一个标志数组,判断是否出现过这种组合 但是这个题始终过不了,不知道为啥 while(1) { f3=(f2*a+f1*b)%7; f1=f2; f2=f3; if(used[f1][f2]) break; else used[f1][f2]=true; result[cnt]=f3; ++cnt; } */ cnt-=2; if(n%cnt) cout<<result[n%cnt]<<endl; else cout<<result[cnt]<<endl; } return 0; }
2050
平面上有n条折线,问这些折线最多能将平面分割成多少块?
我们首先考虑n条直线将平面最多分割成多少块,结论为n*(n+1)/2+1,然后我们将n条折线看成
2*n条直线,那么此时最多将平面分割成2*n(2*n+1)/2+1,由于每一条折线和2条直线相比都少分割了2个平面,所以n条折线就少分割了2*n,即最后的结论为:
2n*(2n+1)/2+1-2n
1465
n个信封,n个封皮,如果所有的信都装错了信封。求所有的信都装错信封,共有多少种不同情况。找递归公式,现在假设有n个信封,那么前N-1个信封可以要么是全部装错,要么有N-2个都装错(如果是N-3个装错,就不符合要求了,无法满足全部装错)。如果前N-1个全部装错,那么第N个信封可以和前N-1的任意一个互换,这样为n-1*f(n-1),如果有N-2个全部装错,那么我们只能把装对的那个与N交换,装对的那个可以是前n-1个的任意一个,这样情况为n-1*f(n-2),所以最后的递推公式为:
f(n)=(n-1)*(f(n-1)+f(n -2)),f1=0,f2=1 (错排公式!!!)
源码:
#include <iostream> using namespace std; int main() { int n,cnt; __int64 fn_1,fn_2,fn; while(scanf("%d",&n)!=EOF) { fn_2=0; fn_1=1; if(1 == n) fn=fn_2; else if(2 == n) fn=fn_1; else { cnt=3; while(cnt<=n) { fn=(cnt-1)*(fn_1+fn_2); fn_2=fn_1; fn_1=fn; ++cnt; } } printf("%I64d\n",fn); } return 0; }
2046
2018
2045
有排成一行的n个方格,用红(Red)、粉(Pink)、绿(Green)三色涂每个格子,每格涂一色,要求任何相邻的方格不能同色,且首尾两格也不同色.求全部的满足要求的涂法.
我们假设已经知道前n-1个方格的图法,现在推出再加一个方格的图法,前n-1个方格可以是首位相同(我们会涂上第n个方格,这样就又满足题意了),前n-1个方格首位不同,前者我们只需要让第n个方格图另外的两种颜色中的一种,即2*f首位相同(n-1),后者的话只有一种图法,就是f首尾不同(n-1),即最后的递推公式为:f首尾不同(n)=f首尾不同(n-1)+2*f首位相同(n-1),再考虑首尾相同的递归公式,很容易得出f首尾相同(n)=f首尾不同(n-1),代码:
#include <iostream> using namespace std; int main() { int n; __int64 fn_1e,fn_1ne,fn_e,fn_ne; while(scanf("%d",&n)!=EOF) { if(1 == n) fn_ne=3; else if(2 == n) fn_ne=6; else { int t=3; fn_1ne=6; fn_1e=0; while(t<=n) { fn_ne=fn_1ne+2*fn_1e; fn_e=fn_1ne; fn_1ne=fn_ne; fn_1e=fn_e; ++t; } } printf("%I64d\n",fn_ne); } return 0; }
hdoj2049
一共有N对新婚夫妇,其中有M个新郎找错了新娘,求发生这种情况一共有多少种可能.
利用上题1465 ,这个题多了个组合,即组合+错排,result=C(n,m)*f[m],先打表
源码:
#include <iostream> using namespace std; __int64 f[22]; __int64 getNum(int n,int m) { __int64 tmp=n,rs=1; //一定要定义为long long for(int i=1;i<=m;i++) { rs*=tmp; --tmp; } tmp=1; for(int j=1;j<=m;j++) tmp*=j; return rs/tmp; } int main() { int t,n,m; __int64 fn,num; //打表 f[1]=0; f[2]=1; for(int i=3;i<=20;i++) f[i]=(i-1)*(f[i-1]+f[i-2]); cin>>t; while(t>0) { cin>>n>>m; num=getNum(n,m); fn=num*f[m]; printf("%I64d\n",fn); --t; } return 0; }