E - Modulo Query(区间取模)

https://vjudge.net/problem/ZOJ-3940

题意:

告诉你 N , M , A 1 . . . A n N,M,A_1...A_n N,M,A1...An,每次询问 Y Y Y [ 0 , M ] [0,M] [0,M]有多少个 X X X满足: X % A 1 % A 2... % A n = Y X\%A1\%A2...\%An=Y X%A1%A2...%An=Y

解析:

套路题, [ 0 , 20 ] % 20 → [ 0 , 19 ] , [ 0 , 0 ] [0,20]\%20 \to [0,19],[0,0] [0,20]%20[0,19],[0,0],相当于每次取模让一个大区间变为两种小区间(第一种区间可能有多个)。

m a p map map记录下 [ 0 , R ] [0,R] [0,R]的个数 n u m num num

每次模上 v a l val val时,在map里面把所有 R > = v a l R>=val R>=val的区间(lower_bound)操作一遍即可。

最后统计答案的时候对所有 Y Y Y排序,因为 R ≥ Y R\geq Y RY的区间都会对 Y Y Y有贡献,所以统计也很简单。

时间复杂度保证:

显然,拆分后的两个区间对下一次拆分都有效的次数不会超过logm次(极限情况每次插成两种差不多大小的区间,长度/2)。

/*
 *  Author : Jk_Chen
 *    Date : 2020-08-15-12.41.37
 */
#include<bits/stdc++.h>
using namespace std;
#define LL long long
#define LD long double
#define rep(i,a,b) for(int i=(int)(a);i<=(int)(b);i++)
#define per(i,a,b) for(int i=(int)(a);i>=(int)(b);i--)
#define mmm(a,b) memset(a,b,sizeof(a))
#define pb push_back
#define pill pair<int, int>
#define fi first
#define se second
void test(){cerr<<"\n";}
template<typename T,typename... Args>void test(T x,Args... args){cerr<<"> "<<x<<" ";test(args...);}
const LL mod=1e9+7;
const int maxn=1e5+9;
const int inf=0x3f3f3f3f;
LL rd(){ LL ans=0; char last=' ',ch=getchar();
    while(!(ch>='0' && ch<='9'))last=ch,ch=getchar();
    while(ch>='0' && ch<='9')ans=ans*10+ch-'0',ch=getchar();
    if(last=='-')ans=-ans; return ans;
}
#define rd rd()
/*_________________________________________________________begin*/

map<int,int>Count;
pill Q[maxn];
int main(){
    int t=rd;
    while(t--){
        int n=rd,m=rd;
        Count.clear();
        Count[m]=1;///[0,m]
        while(n--){
        ///拆分后的两个区间对下一次拆分都有效的次数不会超过logm次
        /// [0,20]%20 -> [0,19] [0,0]
        /// 19->1 20->1 38->1 39->2
        /// [0,19]%20不变
            int val=rd;
            auto it=Count.lower_bound(val-1);
            while(it!=Count.end()){
                int R=(*it).first,num=(*it).second;
                Count.erase(it++);
                int b=(R+1)/val;
                Count[val-1]=(1ll*Count[val-1]+(1ll*b*num)%mod)%mod;
                if((R+1)%val){
                    Count[(R+1)%val-1]=(1ll*Count[(R+1)%val-1]+num)%mod;
                }
            }
        }
        int q=rd;
        rep(i,1,q){
            Q[i]={rd,i};
        }
        sort(Q+1,Q+1+q);
        auto it=Count.end();
        it--;
        LL ans=0,sum=0;
        bool End=0;
        per(i,q,1){
            while(!End){
                int R=(*it).first,num=(*it).second;
                if(R>=Q[i].fi){
                    sum=(sum+1ll*num)%mod;
                    if(it!=Count.begin()){
                        it--;
                        continue;
                    }
                    else{
                        End=1;
                        break;
                    }
                }
                else break;
            }
            ans=(ans+sum*Q[i].se)%mod;
        }
        printf("%lld\n",ans);
    }
    return 0;
}

/*_________________________________________________________end*/
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值