ACM-基础-二分查找+分段 2018浙江省赛ZOJ4029

题目链接

2
3 2
100 1000 10000
100 10
4 5
2323 223 12312 3
1232 324 2 3 5
11366
45619

没有看题目的童鞋,请仔细看完题目再来

/*
1.先将a数组排序

2.求 a/幂 的前缀和
z就是表示对于每个p            需要求出 :每个ai/(ai的幂)的和
对于每个和zi=是a[n-1]/幂1  + a[n-2]/幂2  +...+ a[0]/幂n(幂其实就是个整数)
可能产生的式子有  
a[1]/1 + a[2]/1 + a[3]/1 + a[4]/2 + a[5]/2 + a[6]/2 + a[7]/3 + a[8]/4

设sum[i][j]=a[1]/i+a[2]/i+.....a[j]/i   
针对每个a数组,可以得到任意区间内的a数组整除i的总和
设p^i在a数组里的位置为k,p^(i-1)在a数组里的位置为k-1

我们可以利用区间和sum,O1快速求出k与k-1的区间内a数组整除i的总和
不需要遍历k-1~~k的a数组再求和
这里最差的可能也就是每个a[i]都有不同的幂次结果

3.二分查找k,k-1
upper_bound
lower_bound
自行百度了解一下哦~
*/

代码:

#include<cstdio>
#include<string>
#include<cstring>
#include<algorithm>
using namespace std;
const int maxn=5e5+2;
const int mod=1e9;
long long b[33];  //两个long long 一个int才AC
long long a[maxn];//三个longlong  MLT
int sum[33][maxn];//三个int       SF 
int t,n,m;        //这个zoj有毒
int biao(long long p){
    int i;
    b[0]=1;
    for(i=1;b[i-1]<a[n-1];i++){
        b[i]=b[i-1]*p;
    }
    return i;
}
int main(){
    scanf("%d",&t);
    while(t--){
        scanf("%d %d",&n,&m);
        long long res=0,p;
        for(int i=0;i<n;i++)scanf("%lld",&a[i]);
        sort(a,a+n);
        for(int i=1;i<32;i++){
            for(int j=0;j<n;j++){
                if(j==0)sum[i][j]=a[j]/i;
                sum[i][j]=(a[j]/i+sum[i][j-1])%mod;
            }
        }
        for(int i=1;i<=m;i++){
            long long ans=0;
            scanf("%lld",&p);
            int k=biao(p);
            //printf("k=%d\n",k);
            int low=-1,up;
            for(int j=1;j<k;j++){
                if(b[j]<a[0])continue;
                up=upper_bound(a,a+n,b[j])-a;
                if(up>n)break;//剪枝防TLE
                //up的前一位一定是a数组里的
                //if(b[j]>=a[n-1]&&up==low)break;
                //printf("up=%d  low=%d\n",up,low);
                if(low<0)ans=sum[j][up-1];
                else {
                    ans+=(sum[j][up-1]-sum[j][low-1]+mod)%mod;
                    ans%=mod;
                }
                low=up;
            }
            res+=(i*ans)%mod;
            res%=mod;
        }
        printf("%lld\n",res);
    }
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值