因为我们的服务器暂时不能用了,所以这周大家现在这看,等好了我在放过去。
题目链接
A
从题目中给出的条件可以猜出是用最大公约数,做题的时候要注意自己一星一点的想法,答案往往就藏在这里面。
那为什么是用最大公约数呢?我们知道最大公约数的求法仔细分下来主要有连个步骤:
- g c d ( a , b ) = > g c d ( a % b , b ) gcd(a,b) => gcd(a\% b, b) gcd(a,b)=>gcd(a%b,b)
- g c d ( a % b , b ) = > g c d ( b , a % b ) gcd(a\%b, b) => gcd(b,a\%b) gcd(a%b,b)=>gcd(b,a%b)
题目中给的第一个和第三个条件可以完成第一个步骤。
借助第四个 条件可以得出如下推导: ( b , a ) → ( a + b , a ) → ( a + b , b ) → ( a , b ) (b,a) \rightarrow (a+b,a)\rightarrow(a+b,b)\rightarrow(a,b) (b,a)→(a+b,a)→(a+b,b)→(a,b) 反之亦言,完成第二个步骤。
所以只需要判断前两个数的最大公约数与后两个数的最大公约数是否相同相同则能跳到,不相等则不能跳到。
#include <bits/stdc++.h>
#define pi acos(-1)
#define pb push_back
#define LL long long
#define lson l , m , rt << 1
#define rson m + 1 , r , rt << 1 | 1
#define local freopen("in.txt","r",stdin)
#define input_fast std::ios::sync_with_stdio(false);std::cin.tie(0)
using namespace std;
const int mod = 1e9 + 7;
const int INF=0x7FFFFFFF;
inline void read(int& x){
int flag = 1; char c; while(((c = getchar()) < '0' || c > '9') && c != '-');
c == '-' ? (flag = -1, x = 0) : (x = c - '0');
while((c = getchar()) >= '0' && c <= '9') { x = x * 10 + c - '0'; } x *= flag;
}
int gcd(LL a, LL b){
return b ? gcd(b, a % b) : a;
}
int main(){
input_fast;
//local;
int t; LL a, b, x, y, g1, g2; cin >> t; while(t--){
cin >> a >> b >> x >> y;
g1 = gcd(a, b); g2 = gcd(x, y);
cout << (g1 == g2 ? "Yes" : "No") << endl;
}
return 0;
}
其实第二步的推导过程,就是我们在不借助第三变量的情况下交换两个变量的值的过程。
B
通过我在群里发的篇文章,可以得知,只要某个数的斐波那契表示中没有相邻的一就是最短的斐波那契表示,并且唯一。(具体证明看那篇文章)
如果暴力去解,数据量太大,肯定会超时。
于是我们观察F(n)的特征,
1,
1,
1, 2,
1, 2, 2,
1, 2, 2, 2, 3,
1, 2, 2, 2, 3, 2, 3, 3,
1, 2, 2, 2, 3, 2, 3, 3, 2, 3, 3, 3, 4,
1, 2, 2, 2, 3, 2, 3, 3, 2, 3, 3, 3, 4, 2, 3, 3, 3, 4, 3, 4, 4,
1, 2, 2, 2, 3, 2, 3, 3, 2, 3, 3, 3, 4, 2, 3, 3, 3, 4, 3, 4, 4, 2, 3, 3, 3, ...
上面是F(n)的取值,通过观察我们发现每一行的数的斐波那契表示的位数相同,并且每一行的个数是斐波那契数。
设G(n)对应题目中的G(n), F(n)对应题目中的F(n),H(i)为到第i行共有多少个数,g(i)为到第i行所有F(n)的和
当我们拿到G(n),首先确定n在上面序列的第几行,通过n和H(i)确定n在第几行(可以顺序比较,也可以二分,不过好像二分也不快),假设在第m行 G ( n ) = g ( m − 1 ) + G ( n − H ( m − 1 ) ) + n − H ( m − 1 ) + 1 G(n)=g(m-1)+G(n-H(m-1))+n-H(m-1)+1 G(n)=g(m−1)+G(n−H(m−1))+n−H(m−1)+1 第一项是前m-1项的和,以后的是在求第m行F(n)的和。对于第m行的数来说,位数相同,最高位都是1,所以我们可以把前面的1去掉不就是从1到n-H(m-1)吗,不理解的话写几项就会发现,所以就有后面的式子,注意我们G(n)计的是从1开始的和所以最后要在加1,就是最高位为1后面全是0的情况。这个题就在 ο ( t ∗ log n ) \omicron(t*\log n) ο(t∗logn) 的复杂度下解决了。
剩下的就是预处理H(i)和g(i),对于H(i)可以算出斐波那契数列在求H(i),通过观察会发现H(i)+1就是斐波那契数列往前移几项。而g(i)=g(i-1)+g(i-2)+H(i-2)+1(我的代码中a[i]就是H(i)+1),第i行就是最高位为1,次高位为0,后面随便变化(当然不能有相邻的1)所以就是后面的三项。
这题有很多细节,对细节的处理可以很灵活,比如过的两个人和我的处理方式就不太一样,所以代码并没有很大的帮助,还可能加重你思考的负担,但还给出代码。另外由于这道题比较复杂,所以描述的可能不是太清楚,有疑问的可以单独来问我,或过了的人。
#include <bits/stdc++.h>
#define pi acos(-1)
#define pb push_back
#define LL long long
#define lson l , m , rt << 1
#define rson m + 1 , r , rt << 1 | 1
#define local freopen("in.txt","r",stdin)
#define input_fast std::ios::sync_with_stdio(false);std::cin.tie(0)
using namespace std;
const int mod = 1e9 + 7;
const int INF=0x7FFFFFFF;
inline void read(int& x){
int flag = 1; char c; while(((c = getchar()) < '0' || c > '9') && c != '-');
c == '-' ? (flag = -1, x = 0) : (x = c - '0');
while((c = getchar()) >= '0' && c <= '9') { x = x * 10 + c - '0'; } x *= flag;
}
LL a[100], g[100]; //用于确定n的等级
void init(){
int i;
a[0] = 1; a[1] = 2;
for(i = 2; i <= 90; ++i) a[i] = a[i - 1] + a[i - 2];
g[0] = 0; g[1] = 1;
for(i = 2; i <= 83; ++i) g[i] = g[i - 1] + g[i - 2] + a[i - 2];
}
LL G(LL n){
if(n <= 0) return 0;
int index = upper_bound(a, a + 90, n + 1) - a - 1;
return g[index] + G(n - a[index]) + n - a[index] + 1;
}
int main(){
input_fast;
int t; LL n;
init();
cin >> t; while(t--){
cin >> n;
printf("%lld\n",G(n));
}
return 0;
}
写过后就会发现递归的过程就是我们把n用斐波那契数表示的过程,所以这题还可以用循环来写,有兴趣的同学可以试试。
//循环的代码
#include <bits/stdc++.h>
#define pi acos(-1)
#define pb push_back
#define LL long long
#define lson l , m , rt << 1
#define rson m + 1 , r , rt << 1 | 1
#define local freopen("in.txt","r",stdin)
#define input_fast std::ios::sync_with_stdio(false);std::cin.tie(0)
using namespace std;
const int mod = 1e9 + 7;
const int INF=0x7FFFFFFF;
inline void read(int& x){
int flag = 1; char c; while(((c = getchar()) < '0' || c > '9') && c != '-');
c == '-' ? (flag = -1, x = 0) : (x = c - '0');
while((c = getchar()) >= '0' && c <= '9') { x = x * 10 + c - '0'; } x *= flag;
}
LL a[100], g[100]; //用于确定n的等级
void init(){
int i;
a[0] = 1; a[1] = 2;
for(i = 2; i <= 90; ++i) a[i] = a[i - 1] + a[i - 2];
g[0] = 0; g[1] = 1;
for(i = 2; i <= 83; ++i) g[i] = g[i - 1] + g[i - 2] + a[i - 2];
}
int main(){
input_fast;
int t, index; LL n, ans;
init();
cin >> t; while(t--){
ans = 0; cin >> n;
while(n>0){
index = upper_bound(a, a + 90, n + 1) - a - 1;
n -= a[index]; ans += g[index] + n + 1;
}
printf("%I64d\n", ans);
}
return 0;
}
另外一种循环
cin >> t; while(t--){
i = 83; ans = 0; cin >> n;
while(n>0){
for(; i >= 0; --i) if(a[i] <= n) break;
n -= a[i]; ans += g[i] + n + 1;
}
printf("%I64d\n", ans);
//其它和上面代码一样
C
我们知道每个数都可以表示成一些质因数的乘积, n = p 1 a 1 ∗ p 2 a 2 ∗ p 3 a 3 … n=p_1^{a1}*p_2^{a2}*p_3^{a3}\dots n=p1a1∗p2a2∗p3a3… 而n的约数的个数 = ( a 1 + 1 ) ( a 2 + 1 ) ( a 3 + 1 ) … =(a1+1)(a2+1)(a3+1)\dots =(a1+1)(a2+1)(a3+1)… 。
这里用到了反素数
对于任何正整数x,其约数的个数记做g(x)。例如g(1)=1,g(6)=4.如果某个正整数x满足:
对于任意i(0 < i < x),都有g(i) < g(x),则称x为反素数。
性质一:一个反素数的质因子必然是从2开始连续的质数.
性质二:p=2t1*3t2*5t3*7t4…必然t1>=t2>=t3>=…
所以我们就可以dfs找了,注意代码中用除法判断大小的方法,这是我们在大数据中经常用到的方法。
/* ********************************
Author : danmu
Created Time : 2016年09月09日 星期五 17时54分36秒
File Name : a.cpp
******************************** */
#include <algorithm>
#include <iostream>
#include <cstdlib>
#include <cstring>
#include <iomanip>
#include <string>
#include <vector>
#include <cstdio>
#include <stack>
#include <queue>
#include <cmath>
#include <list>
#include <map>
#include <set>
#define rep(i,x,y) for(int i=x;i<=y;++i)
#define _rep(i,x,y) for(int i=x;i>=y;--i)
#define CL(S,x) memset(S,x,sizeof(S))
#define CP (S1,S2) memcpy(S1,S2,sizeof(S2))
#define ALL(x,S) for(x=S.begin();x!=S.end();++x)
#define ULL unsigned long long
#define PI 3.1415926535
#define INF 0x3f3f3f3f
#define LL long long
const int maxn = 1e6;
const int mod = 1e9 + 7;
const double eps = 1e-8;
const ULL inf=~0ULL;
using namespace std;
int p[16] = {2,3,5,7,11,13,17,19,23,29,31,37,41,43,47,53};
ULL ans,n;
int best;
void dfs(int dept,int limit,ULL res,int num){
if(num>best){
best=num;
ans=res;
}
if(num==best&&ans>res) ans=res;
for(int i=1;i<=limit;++i){
if(n/p[dept]<res) break;
dfs(dept+1,i,res*=p[dept],num*(i+1));
}
}
int main(){
//freopen("in.txt", "r", stdin);
//freopen("out.txt", "w", stdout);
int t;
scanf("%d",&t);
while(t--){
cin>>n;
ans=inf;
best=0;
dfs(0,60,1,1);
cout<<ans<<" "<<best<<endl;
}
return 0;
}
其实我感觉这题不太严谨,但一时有找不出更好的方法(或证明)。