题意:
给你n个盘子,这n个盘子里面分别装着1!到n!重量的食物,对于每一个询问k,找出一个最短的区间,使得区间和 mod 998857459 大于或等于k
盘子数量 n<=1e5 询问次数 m<=1e4
题解:
坑点在于此题模数998857459=4*773*2803 是个合数,因此任何a>=2803 a!=0
因此,只需考虑非0的盘子作为端点,暴力枚举$(max(n,2802))^2$个区间。
我使用的方式是记录阶乘取模非0节点的值和位置,然后求前缀和
再枚举左右端点,记录区间和取模和区间长度
再将区间按照区间和从小到大排序
再把值从小到大的区间从后往前,维护取得大于等于该值所需区间长度的最小值。
询问时在排好序的区间数组上lower_bound,返回该点记录的最小区间长度.
#include<iostream> #include<algorithm> #include<vector> #define MOD 998857459 #define INF 0x3f3f3f3f using namespace std; long long poww[3000]; long long res[100005]; //long long a[100005]; struct Array{ long long val; long long index; long long sum; }a[3000]; long long asiz=0; struct Ans{ long long sum; long long len; long long minlen; friend bool operator < (const Ans &a,const Ans &b){ return a.sum<b.sum; } friend bool operator > (const Ans &a,const Ans &b){ return a.sum>b.sum; } }ans[9000000]; long long anssiz=0; //vector<Ans> ans; int main(){ poww[1]=1; for(long long i=2;i<=2805;i++){ poww[i]=1LL*poww[i-1]*i%MOD; //printf("%lld ",poww[i]); } long long n,m; scanf("%lld %lld",&n,&m); for(long long i=1;i<=n;i++){ long long t; scanf("%lld",&t); if(t<=2805){ asiz++; a[asiz].index=i; a[asiz].val=poww[t]; } } //a[1].sum=a[1].val; for(long long i=1;i<=asiz;i++){ a[i].sum=a[i-1].sum+a[i].val; } //memset(res,INF,sizeof res); for(long long i=1;i<=asiz;i++){ for(long long j=i;j<=asiz;j++){ //ans.push_back(aa); anssiz++; ans[anssiz].len=a[j].index-a[i].index+1; ans[anssiz].sum=(a[j].sum-a[i-1].sum)%MOD; //res[j-i+1]=max(res[j-i+1],a[j].sum-a[i-1].sum); } } sort(ans+1,ans+1+anssiz); ans[anssiz].minlen=ans[anssiz].len; for(long long i=anssiz-1;i>=1;i--){ ans[i].minlen=min(ans[i+1].minlen,ans[i].len); //printf("*%lld %lld ",ans[i].sum,ans[i].minlen); } //for(int i=1;i<=anssiz;i++){ //printf("*%lld %lld %lld ",ans[i].sum,ans[i].len,ans[i].minlen); //} for(long long i=1;i<=m;i++){ long long k; scanf("%lld",&k); Ans tmp; tmp.sum=k; long long aa=lower_bound(ans+1,ans+1+anssiz,tmp)-ans; //printf("%lld ",aa); if(aa==anssiz+1)printf("-1 "); else printf("%lld ",ans[aa].minlen); } return 0; }