2021.10.10 CCPC网络选拔赛(重赛)补题

这篇博客介绍了如何解决Monopoly问题,通过前缀和和模运算找到满足条件的子序列。作者提到,关键在于对正负数情况的讨论以及特殊情况的处理。博客中还展示了C++代码实现,虽然代码可能不够美观,但能有效解决问题。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

没想到啊,再见OI之后还会写博客。
ZXB啊ZXB,要努力练ACM啊。

5.Monopoly

p r e [ i ] pre[i] pre[i] a a a的前缀和 S u m Sum Sum为数组总和
若有解则存在 M M M使得
p r e [ i ] + M ∗ S u m = = x pre[i]+M*Sum==x pre[i]+MSum==x

也就是说 p r e [ i ] ≡ x   ( m o d    S u m ) pre[i] \equiv x \ (mod\ \ Sum) pre[i]x (mod  Sum)
然后对 S u m Sum Sum分情况讨论就好,正数、负数,分别找小于 x x x的最大的 p r e [ i ] pre[i] pre[i]和大于 x x x的最小的 p r e [ i ] pre[i] pre[i]
最后 S u m = 0 Sum=0 Sum=0的情况特判一下就好了

有一说一,口胡还是蛮简单的,就是细节有点烦人qwq(没办法,谁叫我菜呢

代码有点丑,a了就不想改了qwq

#include <bits/stdc++.h>
#define int long long 
using namespace std;

const int maxn=1e6+5;

int N,M,Sum,tot,a[maxn];

map <int,int> mn;
map <int,int> :: iterator iter;

struct node{int id,w;};
inline bool operator <(node a,node b) {
	if (Sum>0) return a.w<b.w||a.w==b.w&&a.id>b.id;
	else return a.w<b.w||a.w==b.w&&a.id<b.id;
}
vector <node> mol[maxn];

inline int read() {
	int ret=0,f=1;
	char ch=getchar();
	for (; !isdigit(ch); ch=getchar()) if (ch=='-') f=-f;
	for (; isdigit(ch); ch=getchar()) ret=ret*10+ch-48;
	return ret*f;
}

void Find1(int x) {//找小于x的最大的pre[i]
	int Ans=-1;
	int ID=mn[(x%Sum+Sum)%Sum];
	int L=0,R=mol[ID].size()-1;
	while (L<=R) {
		int Mid=L+(R-L>>1);
		if (mol[ID][Mid].w<=x) Ans=Mid,L=Mid+1;
		else R=Mid-1;
	}
	if (~Ans) printf("%lld\n",(x-mol[ID][Ans].w)/Sum*N+mol[ID][Ans].id);
	else puts("-1");
}

void Find2(int x) {//大于x的最小的pre[i]
	int Ans=-1;
	Sum=-Sum;
	int ID=mn[(x%Sum+Sum)%Sum];
	int L=0,R=mol[ID].size()-1;
	while (L<=R) {
		int Mid=L+(R-L>>1);
		if (mol[ID][Mid].w>=x) Ans=Mid,R=Mid-1;
		else L=Mid+1;
	}
	if (~Ans) printf("%lld\n",(mol[ID][Ans].w-x)/Sum*N+mol[ID][Ans].id);
	else puts("-1");
	Sum=-Sum;
}

signed main() {
	for (int T=read(); T--; mn.clear()) {
		N=read(),M=read(),a[0]=0;
		for (int i=1; i<=N; ++i) a[i]=a[i-1]+read();//a就是pre
		Sum=a[N];
		if (Sum==0) {
			for (int i=1; i<=N; ++i) if (mn[a[i]]==0) mn[a[i]]=i;
			for (int i=1; i<=M; ++i) {
				int x=read();
				if (x==0)
					puts("0");
				else
					if (mn[x]!=0) printf("%lld\n",mn[x]);
					else puts("-1");
			}
			continue;
		} else {
			bool flg=(Sum>0);
			if (Sum<0) Sum=-Sum;
			tot=0;
			for (int i=1,tmp; i<=N; ++i) {//离散
				tmp=(a[i]%Sum+Sum)%Sum;
				if (mn[tmp]==0) mn[tmp]=++tot;
				mol[mn[tmp]].push_back((node){i,a[i]});
			}
			if (!flg) Sum=-Sum;
			for (iter=mn.begin(); iter!=mn.end(); iter++) {
				sort(mol[mn[iter->first]].begin(),mol[mn[iter->first]].end());
			}
			for (int i=1; i<=M; ++i) {
				int x=read();
				if (x==0)
					puts("0");
				else
					if (flg) Find1(x);else Find2(x);
			}
			for (iter=mn.begin(); iter!=mn.end(); iter++) {
				mol[mn[iter->first]].clear();
			}
		}
	}
	return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值