2021CCPC网络赛重赛1005 Monopoly题解
题意
多组数据,每组数据一个长度为n的数组a,m个询问。记数组a的前缀和为sum,对每个询问x,找到最小的长度len,使得x=sum[len%n]+sum[n]*(len/n)
分析
- 考虑暴力,每次询问暴力枚举前缀sum[i],i from 0 to n-1,再检查x-sum[i] 能否被sum[n]整除即可,复杂度O(n2)。
- 对于公式 x=sum[len%n]+sum[n]*(len/n) 等号两边同时对sm[n]取模,得到x%sum[n]=sum[len%n]%sum[n]。即对于每个询问x,合法的前缀要满足这个式子。
- 因为sum[i]有n个,sum[len%n]%sum[n]最多也只有n个,所以可以提前存储sum[len%n],对于每次询问x只找合法的sum[len%n]来加速枚举。
- 上面的加速显然可以被特定数据卡回暴力复杂度。但我们要最小化len,而在 len=len%n+(len/n)n 中,显然len%n小于 (len/n)n,所以我们要最小化len/n,也就是要最小化 x=sum[len%n]+sum[n]*(len/n) 中sum[n]的使用次数所以当sum[n]>0时sum[len%n]要为最接近x的数值。
- 每次询问时,找到最大的小于x的sum[len%n],且满足x%sum[n]=sum[len%n]%sum[n]。所以这里就可以给sum[len%n]排序了,以**sum[len%n]%sum[n]**为第一关键字,**sum[len%n]**为第二关键字排序,每次就可以通过二分来查找了。该次询问的答案即为
len=len%n+(len/n)n
小细节
- sm[n]=0要特判
- 考场上傻傻的直接对sm[n]取模导致本质相同的sum[i]%sm[n]有的正有的负,故x%sm[n]忽略了一部分可取的sm[i]。所以要统一sum[i]%sm[n]为正,但这样又要统一sum[n]为正。故sum[n]<0时,需要将所有的sum[i]和x取反来保证sum[n]>0且答案正确。
- 对于sum和sum%sum[n]均相同的sum,需要只保留len%n最小的那个,其余舍弃,便于二分。
代码
#include<cstdio>
#include<cstring>
#include<algorithm>
#define ll long long
using namespace std;
const int mx=100005;
int t,n,m,cnt;
struct node{
ll sm,id;
int ip;
}po[mx];
inline ll read()
{
ll x=0,f=1;char ch=getchar();
while(ch>'9'||ch<'0'){if(ch=='-') f=-1;ch=getchar();}
while(ch>='0'&&ch<='9'){x=(x<<3)+(x<<1)+ch-48;ch=getchar();}
return x*f;
}
inline bool cmp(node a,node b){
if(a.id==b.id){
if(a.sm==b.sm) return a.ip<b.ip;
return a.sm<b.sm;
}
return a.id<b.id;
}
int main()
{
// freopen("1005.in","r",stdin);
// freopen("a.out","w",stdout);
t=read();
for(int op=1;op<=t;++op){
n=read();m=read();
ll sm=0,p,rp;
bool flag=false;
po[0].sm=0;po[0].ip=0;
for(int i=1;i<n;++i){
sm+=read();po[i].sm=sm;
po[i].ip=i;
}
sm+=read();
if(sm<0){
flag=true;sm=-sm;
for(int i=0;i<n;++i) po[i].sm=-po[i].sm;
}
for(int i=0;i<n;++i) po[i].id=sm!=0?(po[i].sm%sm+sm)%sm:0;
sort(po,po+n,cmp);cnt=0;
for(int i=1;i<n;++i) if(po[i].id!=po[i].id||po[i].sm!=po[i-1].sm) po[++cnt]=po[i];
if(sm==0){
for(int i=1;i<=m;++i){
p=read();
int l=0,r=cnt,mid;
while(l<r){
mid=(l+r)>>1;
if(po[mid].sm<p) l=mid+1;
else r=mid;
}
if(po[l].sm!=p) printf("-1\n");
else printf("%d\n",po[l].ip);
}
}
else
for(int i=1;i<=m;++i){
p=read();if(flag) p=-p;
rp=(p%sm+sm)%sm;
int l=0,r=cnt,mid;
while(l<r){
mid=(l+r+1)>>1;
if(po[mid].id<rp) l=mid;
else if(po[mid].id>rp) r=mid-1;
else if(po[mid].sm>p) r=mid-1;
else l=mid;
}
if(po[l].id!=rp) printf("-1\n");
else if(sm>0&&po[l].sm>p) printf("-1\n");
else if(sm<0&&po[l].sm<p) printf("-1\n");
else printf("%lld\n",po[l].ip+n*(p-po[l].sm)/sm);
}
}
return 0;
}