ICode9

精准搜索请尝试: 精确搜索
首页 > 其他分享> 文章详细

2021CCPC网络赛重赛1005 Monopoly题解

2021-10-10 20:33:36  阅读:259  来源: 互联网

标签:len% Monopoly 题解 sum 赛重赛 mid len sm po


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;
}

标签:len%,Monopoly,题解,sum,赛重赛,mid,len,sm,po
来源: https://blog.csdn.net/vermouth_1412/article/details/120691163

本站声明: 1. iCode9 技术分享网(下文简称本站)提供的所有内容,仅供技术学习、探讨和分享;
2. 关于本站的所有留言、评论、转载及引用,纯属内容发起人的个人观点,与本站观点和立场无关;
3. 关于本站的所有言论和文字,纯属内容发起人的个人观点,与本站观点和立场无关;
4. 本站文章均是网友提供,不完全保证技术分享内容的完整性、准确性、时效性、风险性和版权归属;如您发现该文章侵犯了您的权益,可联系我们第一时间进行删除;
5. 本站为非盈利性的个人网站,所有内容不会用来进行牟利,也不会利用任何形式的广告来间接获益,纯粹是为了广大技术爱好者提供技术内容和技术思想的分享性交流网站。

专注分享技术,共同学习,共同进步。侵权联系[81616952@qq.com]

Copyright (C)ICode9.com, All Rights Reserved.

ICode9版权所有