Gym - 101612L Little Difference(二分)

题目链接:https://vjudge.net/problem/Gym-101612L

题目大意:给出一个n,1 <= n <= 10^18,生成所有满足条件的数列,这些数列需要满足数列内的所有项相乘等于n,数列内的任意两项只差必须小于等于1。若这样的数列是有限个,全部输出,否则输出-1。

首先这题特别坑,光题意就看了好久好久好久……看到自闭,最后终于看懂,然后想了一会发现能做,接下来就陷入无限的找bug中,最后发现有一个特例没判断。不扯了,看题。

 

分析:首先我们可以很轻易的知道 1 应该是输出 -1 ,还可以发现 2 也是 -1,因为可以有这么个数列:2,1,1,……,1 这是无限个的,所以也是 -1 ,还有一个特别坑的地方,所有的 2 的次幂都是 -1,道理和 2 一样,但是不容易想到这个特例。

 

现在是对于一般情况,我们看到数的范围很大,n大约在 2^63 次方以内,而对于一组合法的数列只有两种可能:1,只由 a 组成。2,只由 a 和 a+1 组成。这样一来我们想一下,对于一个合法的数列,这个数列的长度是这个数列独有的,不可能有另一个合法的数列和这个长度一样。

 

现在我们假设知道了这个数列长度,我们可以二分出来这个 a,有了这个a,我们就可以生成这个合法的数列。

 

我们怎么二分这个a呢?我们枚举数列的长度 k ,若在这个长度下有一个合法的数列,那么一定有 a^i * (a+1)^k-j == n,这个a怎么找呢,二分找 a^k 小于等于 n 又最接近 n 的 a,a可能的取值只有这一个,那么数列的另一项也就出来了,a+1

 

我们前面是基于枚举的数组长度下有一个合法的数列这个条件下二分出来的 a ,那我们怎么判断这个 a 是否合法呢?我们已经知道如果合法必须满足 a^i * (a+1)^k-j == n ,那么我们可以枚举这个 i ,那么 k-i 就是 a+1 的系数,这样一来我们就可以判断是否合法了。

 

不难算出上述算法在的复杂度是O((logn)^3),最坏情况下 63^3 ,妥妥的。。。

 

代码如下:

 1 #include<bits/stdc++.h>
 2 using namespace std;
 3 
 4 typedef long long LL;
 5 
 6 LL n;
 7 
 8 bool check(LL a, int b) {
 9     LL ans = 1;
10     for(int i = 1; i <= b; ++i) {
11         if(n/ans >= a) ans *= a;
12         else return false;
13     }
14     return true;
15 }
16 
17 bool judge(int a, LL b, int c, LL d) {
18     LL ans = 1;
19     for(int i = 1; i <= a; ++i) {
20         if(n/ans >= b) ans *= b;
21         else return false;
22     }
23     for(int i = 1; i <= c; ++i) {
24         if(n/ans >= d) ans *= d;
25         else return false;
26     }
27     if(ans == n) return true;
28     return false;
29 }
30 
31 LL ANS[100][100];
32 int tot;
33 void addans(int a, LL b, int c, LL d) {
34     LL& len = ANS[tot][0];
35     for(int i = 1; i <= a; ++i) ANS[tot][++len] = b;
36     for(int j = 1; j <= c; ++j) ANS[tot][++len] = d;
37     tot++;
38 }
39 
40 int main() {
41     freopen("little.in", "r", stdin);
42     freopen("little.out", "w", stdout);
43 
44     scanf("%lld", &n);
45 
46     // 如果n是2的次幂,那么就是输出-1
47     LL m = n;
48     while(m%2 == 0) m/=2;
49     if(m == 1) {
50         printf("-1\n");
51         return 0;
52     }
53 
54     for(int len = 1; len <= 64; ++len) {
55         LL l = 2, r = 1e18, ans = -1;
56         while(l <= r)  {
57             LL mid = (l+r)/2;
58             if(check(mid, len)) {
59                 ans = mid;
60                 l = mid+1;
61             }else {
62                 r = mid-1;
63             }
64         }
65 
66         if(ans == -1) break;
67 
68         for(int i = 1; i <= len; ++i) {
69             if(judge(i, ans, len-i, ans+1))
70                 addans(i, ans, len-i, ans+1);
71         }
72     }
73     printf("%d\n", tot);
74     for(int i = 0; i < tot; ++i) {
75         printf("%lld", ANS[i][0]);
76         for(int j = 1; j <= ANS[i][0]; ++j) printf(" %lld", ANS[i][j]);
77         printf("\n");
78     }
79 }
View Code

 

posted @ 2018-09-18 22:54  DyastySun  阅读(232)  评论(0编辑  收藏  举报