Codeforces Gym 101775D Mr. Panda and Geometric Sequence(2017-2018 ACM-ICPC Asia East Continent League Final,D题,枚举剪枝)
题意 给定$2*10^{5}$组询问,每个询问求$l$到$r$之间有多少个符合条件的数
如果一个数小于等于$10^{15}$, 并且能被分割成一个至少有$3$项的递增等比数列(公比可以不为整数)
那么这个数是符合条件的。
比赛的时候我大概觉得这应该不是数位DP,是一个比较trick的枚举题。
但是我总感觉哪个地方不太对,或者说是没有写这道题的意识,一直瘫在那里。
今天AC了这个题之后真的后悔莫及,但是一点用都没有。
从至少有$3$项这个条件入手。
假设数列只有$3$项。
因为数列递增,所以第二项一定不超过$10^{5}$,
所以等比数列的公比
$\frac{q}{p} <= \frac{a_{2}}{a_{1}} <= a_{2} <= 10^{5}$
设第一项为$kp^{2}$, 第二项为$kpq$, 第三项为$kq^{2}$
那么$kpq <= 10^{5}$,即$pq <= 10^{5}$;
枚举符合条件的$p$和$q$,发现枚举量不超过$4*10^{5}$;
在这个基础上枚举$k$,然后求出整个数列,并考虑那些项数大于$3$的数列,
最后sort一下二分查找就可以了。
#include <ctime> #include <cstdio> #include <cstring> #include <iostream> #include <algorithm> using namespace std; #define rep(i, a, b) for (int i(a); i <= (b); ++i) #define dec(i, a, b) for (int i(a); i >= (b); --i) typedef long long LL; const int N = 1e5 + 10; LL a[N * 100]; LL ten[20]; LL l, r; int cnt = 0; int T, ca = 0; int solve(LL x){ return upper_bound(a + 1, a + cnt + 1, x) - a - 1; } inline int calc(LL x){ int ret = 0; for (; x; x /= 10) ++ret; return ret; } LL mer(LL x, LL y){ return x * ten[calc(y)] + y; } int main(){ ten[0] = 1ll; rep(i, 1, 18) ten[i] = ten[i - 1] * 10ll; rep(p, 1, 1e5){ rep(q, p + 1, 1e5){ if (1ll * p * q >= 1e5) break; if (__gcd(p, q) > 1) continue; rep(k, 1, 1e5 / p / q){ LL x = 1ll * k * p * p; LL y = 1ll * k * p * q; LL z = 1ll * k * q * q; int cnt_len = calc(x) + calc(y) + calc(z); if (cnt_len > 15) break; LL now = mer(mer(x, y), z); a[++cnt] = now; while (true){ if (calc(z) >= 9) break; if (z * z % y > 0) break; LL nw = z * z / y; int nwlen = calc(nw); if (cnt_len + nwlen > 15) break; cnt_len += nwlen; now = mer(now, nw); a[++cnt] = now; y = z; z = nw; } } } } sort(a + 1, a + cnt + 1); scanf("%d", &T); while (T--){ scanf("%lld%lld", &l, &r); printf("Case #%d: %d\n", ++ca, solve(r) - solve(l - 1)); } return 0; }