原问题几乎相当于求1 ~ n的区间内,有多少个数是>=k的。 但是有一点不同的是,这些数是1 ~ n最小素因子。 直接上普通平衡树Treap。 有一点要注意的是,k在平衡树内不存在的情况,比赛的时候就因为这个我一直wawawa.....呜呜呜了

#include <bits/stdc++.h>
using namespace std;
#define int long long
const int N = 1e7 + 5, INF = 1e9 + 7;
struct node{
    int r, k;
    int pos;
    bool operator < (const node &p) const {
        return r < p.r;
    }
} a[N];
int p[N];
int v[N];
int cnt = 0;
void init(int n)
{
    for (int i = 2; i <= n; i++){
        if (v[i]==0) {
            p[++cnt] = i;
            v[i] = i;
        }
        for (int j = 1; j <= cnt; j++){
            if(p[j] > v[i] || p[j] > n / i)break;
            v[i * p[j]] = p[j];
        }
    }
    /* for (int i = 1; i <= 15; i++) cout << v[i] << " ";
    cout << endl; */
}
int ans[N];
int n;
struct Node
{
    int l, r;
    int k;
    int val;//堆中的编号
    int cnt, size;
}tr[N];
int root, idx;
void pushup(int u)
{
    tr[u].size = tr[tr[u].l].size + tr[tr[u].r].size + tr[u].cnt;//上传节点信息,更新size
}
int new_node(int k)
{
    tr[ ++ idx].k = k;
    tr[idx].val = rand();//尽量随机,随手给个就行
    tr[idx].cnt = 1;
    tr[idx].size = 1;
    return idx;
}
void zig(int &u)//左右旋,没啥好说的,自己在纸上画一下就知道了
{
    int q = tr[u].l;
    tr[u].l = tr[q].r;
    tr[q].r = u;
    u = q;
    pushup(tr[u].r);
    pushup(u);//最后一定要记得上传,不然完了
}
void zag(int &u)
{
    int q = tr[u].r;
    tr[u].r = tr[q].l;
    tr[q].l = u;
    u = q;
    pushup(tr[u].l);
    pushup(u);
}
void build()//建树操作,为了正确性增加两个哨兵,防止越界
{
    new_node(-INF), new_node(INF);
    root = 1, tr[1].r = 2;//初始化一下
    pushup(root);//上传信息
    if(tr[1].val< tr[2].val) zag(root);//不平衡了就旋转
}
void insert(int &u, int k)
{
    if(u == 0) u = new_node(k);//如果走到空了,就新建
    else
    {
        if(tr[u].k == k)//如果找到了相同的节点,就cnt++
        {
            tr[u].cnt ++;
        }
        else
        {
            if(tr[u].k > k)//否则看看是在左边还是在右边
            {
                insert(tr[u].l, k);
                if(tr[tr[u].l].val > tr[u].val) zig(u);//不平衡立马调整
            }
            else
            {
                insert(tr[u].r, k);
                if(tr[tr[u].r].val > tr[u].val) zag(u); 
            }
        }
    }
    pushup(u);//最后上传一下,是不是和线段树有点像啊?
}
void del(int &u, int k)//删除操作
{
    if(u == 0) return ;//如果没了说明节点不存在,就不管了。
    if(tr[u].k == k)//如果找到了这个点
    {
        if(tr[u].cnt > 1) tr[u].cnt --;//大于一好说,直接cnt --
        else//不大于一
        {
            if(tr[u].l || tr[u].r)//先看看是不是叶节点
            {
                if(!tr[u].r || tr[tr[u].l].val) 
                {
                    zig(u);
                    del(tr[u].r, k);//记得维护平衡哦
                }
                else
                {
                    zag(u);
                    del(tr[u].l, k);
                }
            }
            else u = 0;//是的话不用考虑平衡问题,直接删就是了
        }
    }
    else if(tr[u].k > k) del(tr[u].l, k);//如果没有找到就判断一下在左右两边的哪一边
    else del(tr[u].r, k);//找一下
    pushup(u);//上传更改
}
int get_rank(int u, int k)
{
    if(u == 0) return 0;//是0随便返回就行
    if(tr[u].k == k) return tr[tr[u].l].size + 1;//相等了那排名应该就是左边的数量加上自己
    if(tr[u].k > k) return get_rank(tr[u].l, k);//大了找左边
    return tr[tr[u].l].size + tr[u].cnt + get_rank(tr[u].r, k);//找右边
}
bitset<N> book;
signed main()
{
    cin >> n;
    for (int i = 1; i <= n; i++){
        scanf("%lld%lld", &a[i].r, &a[i].k);
        a[i].pos = i;
    }
    sort(a + 1, a + 1 + n);
    init(N);
    int now = 2;
    build();
    for (int i = 1; i <= n; i++){
        while(now<=a[i].r){
            insert(root, v[now]);
            book[v[now]] = 1;
            now++;
        }
        int rk = get_rank(root, a[i].k) - 1;
        if(book[a[i].k]) ans[a[i].pos] = a[i].r - 1 - (rk - 1);
        else ans[a[i].pos] = a[i].r - 1 - rk;
    }
    for (int i = 1; i <= n; i++) printf("%lld\n", ans[i]);
    return 0;
}