A.一个部族,一个民族,一个弗雷尔卓德(素数筛+线段树)

“一个部族,一个民族,一个弗雷尔卓德。”

Time limit1000 ms
题目链接https://csustacm.fun/problem/1083
Description

寒冰射手艾希新学会了一个技能,艾希通过这个技能成为了一名声名远扬的神箭手,从此再也无人敢侵犯弗雷尔卓德!

这个技能的描述如下(假设英雄联盟内的每个人都有一个编号):

假设艾希有x-1(x>=2)个敌人,每个敌人的编号分别为1~x-1,那么艾希的编号就是x。艾希每次使用这个技能,那么对于某个敌人,如果这个敌人的编号的最小素因子小于等于艾希的编号的最小素因子,那么艾希能对他造成致命一击。

现在假设已知有tt场战争,每场战争有x-1个敌人,艾希想知道她每场战争使用这个技能能对多少个敌人造成致命一击,由于这个数目太大,她无法计算所以希望你编写一个程序来帮她计算这个结果。

一个x的最小素因子:能够整除xx的最小素数。比如2和4的最小素因子是2,3的最小素因子是3。我们知道1不是素数,但是为了题目的完整性,在这里我们定义1的最小素因子为1。

Input

一个正整数t(t<=1000000),表示有tt组数据。

每组数据输入一个整数x(2<=x<=1000000),表示艾希的编号为x,且敌人数量为x−1。

Output

对于每组数据输出一个整数ans,表示艾希在这场战争中使用该技能能对ans个敌人造成致命一击。

Sample Input 1

2
2
6
Sample Output 1

1
3
Hint

对于6这个数据,我们知道1~6的每个数的最小素因子依次为1、2、3、2、5、2,因此编号为1、2、4的敌人将会受到致命一击。

这题只要注意要求是小于等于就好了。另外就是要在埃氏筛的同时求最小素因子,这样就不会超时。至于线段树用于存下当前最小素因子出现的次数就好了,每次询问1到ans[i](i的最小素因子),下面给出代码实现。
100万次操作,本以为会超时,线段树还是很nb的

#include <cstdio>
#include <cmath>
#include <cstring>
#define ll long long
#define mac 1000020
#define debug(n) printf("%d ",n)
struct st
{
	int l,r,w;
}tree[mac*4];
ll ans[mac],prim[mac],vis[mac],vip[mac],sum[mac],ans2;
int l=1,r;
void build(int now,int l,int r);
void ask(int now);
void change(int s,int now);
int main()
{
	ll t,num=1;
    ll m=sqrt(mac);
    for (ll i=2; i<=m; i++)
        if (!vis[i])
          for (ll j=i*i; j<mac; j+=i){
          	vis[j]=1;
          	if (!ans[j]) ans[j]=i;         // 筛出素数的同时标记j的最小素因子
		  } 
	ans[1]=1;
	for (int i=2; i<=mac; i++) if (!vis[i]) ans[i]=i;
    build(1,1,mac);
	for (ll i=1; i<mac; i++){
		ans2=0;
		r=i;
		ask(1);
		sum[i]=ans2;                      
		change(ans[i],1);            //更新ans[i]的值
    }
    scanf ("%lld",&t);
    while(t--){
        ll x;
        scanf ("%lld",&x);
        printf ("%lld\n",sum[x]);
    }
    return 0;
}
void build(int now,int l,int r)
{
    tree[now].r=r,tree[now].l=l;
    if (l==r){
        tree[now].w=0;                      //建空树
        return;
    }
    int mid=(l+r)>>1;
    build (now*2,l,mid);
    build (now*2+1,mid+1,r);
    tree[now].w=tree[now*2].w+tree[now*2+1].w;
}
void ask(int now){
    if (tree[now].l>=1 && tree[now].r<=ans[r]){
        ans2+=tree[now].w;             //询问ans[i]之前的数有多少个
        return;
    }
    int mid=(tree[now].l+tree[now].r)>>1;
    if (1<=mid) ask(now*2);
    if (ans[r]>mid) ask(now*2+1);
}
void change(int s,int now)
{
   if (tree[now].l==tree[now].r){
        tree[now].w++;                       //当前该最小素因子出现,次数加一
        return;
    }
    int mid=(tree[now].l+tree[now].r)>>1;
    if (s<=mid) change(s,now*2);
    else change(s,now*2+1);
    tree[now].w=tree[now*2].w+tree[now*2+1].w;
}
  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值