比赛前三天蒟蒻打了一个暴力想要看看数据强度的时候,发现正解挂了!!!
然后调了出题人一个下午终于肝出来了..
(蒟蒻出题人再也不出毒瘤数据结构题了..)
首先我们可以预处理出11~10^7107所有的数能分解的质因子个数..
这个可以用欧拉线性筛筛出来..虽然这个并不是积性函数..
我们可以预处理出斐波那契数列的前4040项..这么多已经够用了..
然后我们把第ii项改成原来的第ii项减去第i-1i−1项..
这样子就变成了与上一个隔多少个就再取一个数了..
比如说原来的斐波那契数列是:1, 2, 3, 5, 8...
然后我们把它魔改成:1, 1, 1, 2, 3...(实际上是右移了两下..)
对于第一行来说,上面五个数就表示: 第一个要选的是上一个(没有为00)的后一位: 11
第二个要选的就是上一个(11)的后一位: 22
第三个要选的就是上一个(22)的后一位: 33
第四个要选的就是上一个(33)的后两位: 55
第五个要选的就是上一个(55)的后三位: 88
...
然而还是斐波那契数列..
这个有个\piπ(pi)用啊!!!
当然有用啊..
改成了这样后我们就可以搞6060的暴力辣!!!
首先我们发现前10的分组是这样的:
1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | ... |
---|---|---|---|---|---|---|---|---|---|---|
1 | 1 | 1 | 2 | 1 | 2 | 2 | 1 | 3 | 2 | ... |
没有发现什么吗??
然后让我们把之前的候选的东西放进去:
数字 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | ... |
---|---|---|---|---|---|---|---|---|---|---|---|
归属 | 1 | 1 | 1 | 2 | 1 | 2 | 2 | 1 | 3 | 2 | ... |
第11行的候选 | 1 | 1 | 1 | 2 | 1 | 3 | 2 | 1 | 5 | 4 | ... |
第22行的候选 | (1)- | (1)- | (1)- | 1 | (1)- | 1 | 1 | (1)- | 2 | 1 | ... |
第33行的候选 | (1)- | (1)- | (1)- | (1)- | (1)- | (1)- | (1)- | (1)- | 1 | (1)- | ... |
当然,这里面打'-'的表示不可能选到的..括号表示没有改变(或者说是无关的)候选..
然后一个很显然的一点: 一个数字如果属于前面的行是不可能影响到后面的行的候选的..
而且从数字为10的时候我们可以看出:如果当前候选的有多个为1的(第22行和第33行),优先选择行数小的(第22行),并且行数大的(第33行)候选不改变(虽然这里貌似看不出来,但是可以自己多写写就可以看出来了)..并且不是可选行且比当前选择行(第22行)小的(第11行)候选也要减..
不信可以自己试试..Markdown的表格真难写..
然后这个就可以离线询问然后O(NK)O(NK)模拟了..
至于正解..
我们发现这个就是一个区间最小值,区间减法,这个直接用线段树搞就好了..
附上一个小优化:如果当前10^4104的行内候选都大于11(假设是xx),那么后面x-1x−1的数都肯定不是这10^4104行内的东西..所以可以直接在枚举NN的时候加上x-1x−1,然后11~10^4104区间减去x-1x−1就好了..
附上丑陋而异常臃肿的代码..
#include <bits/stdc++.h>
using namespace std;
#define reg register
#define enter putchar('\n')
#define ls (Node << 1)
#define rs ((Node << 1) | 1)
#define MAXN 5000000
#define MAXT 1000000
#define MAXK 10000
const char FI[] = "queue.in";
const char FO[] = "queue.out";
typedef short sh;
inline int Min(reg int a, reg int b) { return a < b ? a : b; }
inline int read() {
reg int s = 0, t = 1; reg char ch = getchar();
while(ch > '9' || ch < '0') t *= ch == '-' ? -1 : 1, ch = getchar();
while(ch >= '0' && ch <= '9') s = s * 10 + ch - '0', ch = getchar();
return s * t;
}
struct Node {
sh k;
int n;
int id;
}Ask[MAXT + 1];
bool operator < (Node a, Node b) {
return a.n < b.n;
}
int T;
sh s[MAXN + 1];
int res[MAXT + 1];
int fib[50] = {1, 1};
int prime[40 * MAXK];
bitset<MAXN + 1>Check;
int Id[MAXK << 5];
int pos[MAXK << 5];
int tag[MAXK << 5];
int Tree[MAXK << 5];
inline void init() {
reg int tot = 0;
for(reg int i = 2; i <= MAXN; i++) {
if(!Check[i]) prime[++tot] = i, s[i] = 1;
for(reg int j = 1; j <= tot && i * prime[j] <= MAXN; j++) {
Check[i * prime[j]] = 1, s[i * prime[j]] = s[i] + 1;
if(i % prime[j] == 0) break;
}
}
for(reg int i = 2; i < 50; i++) fib[i] = fib[i - 1] + fib[i - 2];
for(reg int i = 49; i > 1; i--) fib[i] -= fib[i - 1];
T = read();
for(reg int i = 1; i <= T; i++)
Ask[i].n = read(), Ask[i].k = read(), Ask[i].id = i;
sort(Ask + 1, Ask + T + 1);
}
inline void build(reg int Node, reg int L, reg int R) {
Tree[Node] = 1;
if(L == R) {
Id[Node] = L, pos[Node] = Node;
return ;
}
reg int mid = (L + R) >> 1;
build(ls, L, mid), build(rs, mid + 1, R);
pos[Node] = pos[ls];
}
inline void Add(reg int Node, reg int L, reg int R, reg int l, reg int r, reg int k) {
if(l <= L && R <= r) {
Tree[Node] += k, tag[Node] += k;
return ;
}
Tree[ls] += tag[Node]; tag[ls] += tag[Node];
Tree[rs] += tag[Node]; tag[rs] += tag[Node];
tag[Node] = 0;
reg int mid = (L + R) >> 1;
if(l <= mid) Add(ls, L, mid, l, r, k);
if(r > mid) Add(rs, mid + 1, R, l, r, k);
Tree[Node] = min(Tree[ls], Tree[rs]);
pos[Node] = (Tree[Node] == Tree[ls] ? pos[ls] : pos[rs]);
}
int main() {
init();
reg int M = Ask[T].n;
reg int id = 1;
reg int g[MAXK + 1];
reg int ans[MAXK + 1]; memset(ans, -1, sizeof(ans));
for(reg int i = 1; i <= 10000; i++) g[i] = 1;
build(1, 1, 10000);
for(reg int i = 1, Mi, add, ID; id <= T && i <= M; i++) {
Mi = pos[1], ID = Id[Mi];
Add(1, 1, 10000, ID, ID, 0);
add = Tree[Mi];
Add(1, 1, 10000, 1, ID, -add);
if(add > 1 && ID < 10000)
Add(1, 1, 10000, ID + 1, 10000, 1 - add);
Add(1, 1, 10000, ID, ID, fib[++g[ID]]);
while(id <= T && Ask[id].n < i + add - 1) res[Ask[id].id] = ans[Ask[id].k], id++;
i += add - 1;
if(i > M || id > T) break;
ans[ID] += s[i] + (ans[ID] < 0);
while(id <= T && Ask[id].n <= i) res[Ask[id].id] = ans[Ask[id].k], id++;
}
for(reg int i = 1; i <= T; i++) printf("%d", res[i]), enter;
return 0;
}
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 地球OL攻略 —— 某应届生求职总结
· 周边上新:园子的第一款马克杯温暖上架
· Open-Sora 2.0 重磅开源!
· 提示词工程——AI应用必不可少的技术
· .NET周刊【3月第1期 2025-03-02】