【dtoj#3899】Podzielno

传送门

题目描述

B B B进制数,每个数字 i ( i = 0 , 1 , . . . , B − 1 ) i(i=0,1,...,B-1) i(i=0,1,...,B1) a [ i ] a[i] a[i] 个。你要用这些数字组成一个最大的 B B B 进制数 X X X (不能有前导零,不需要用完所有数字),使得 X X X B − 1 B-1 B1 的倍数。
q q q 次询问,每次询问 X X X B B B 进制下的第 k k k 位数字是什么(最低位是第 0 0 0 位)。

输入格式

第一行包含两个正整数  B ( 2 ≤ B ≤ 1 0 6 ) , q ( 1 ≤ q ≤ 1 0 5 ) B(2\le B \le 10^6),q(1 \le q \le 10^5) B(2B106),q(1q105)
第二行包含 B B B 个正整数 a [ 0 ] , a [ 1 ] , a [ 2 ] , . . . , a [ B − 1 ] , 0 < a [ i ] ≤ 1 0 6 a[0],a[1],a[2],...,a[B-1],0 < a[i] \le 10^6 a[0],a[1],a[2],...,aB10<a[i]106
接下来 q q q 行,每行一个整数 k ( 0 ≤ k ≤ 1 0 18 ) k(0 \le k \le 10^{18} ) k(0k1018),表示一个询问。

输出格式

输出q行,每行一个整数,依次回答每个询问,如果那一位不存在,请输出-1。

样例输入
3 3
1 1 1
0
1
2
样例输出
0
2
-1
数据范围与提示

输入格式中已给出

题解

需要明确一个性质:

a ∗ B i m o d ( B − 1 ) = a a*B^{i}mod(B-1)=a aBimod(B1)=a

既然如此,题目就变成了选取一些数字,让它们之和对 B − 1 B-1 B1取模等于 0 0 0注意,题目已经告诉我们 a [ i ] > 0 a[i]>0 a[i]>0,说明每个数字都至少有一个,那么直接去掉那个取模结果即可。剩下数字的排列自然是从大到小。

代码:

#include<iostream>
#include<cstdio>
#include<map>
#include<algorithm>
typedef long long ll;
using namespace std;
ll B,q,sum,a[1000005],wss,k,lst[1000005],lstr[1000005],tot;
map <ll,ll> exc;
int main(){
	scanf("%lld%lld",&B,&q);
	const ll mod=B-1;
	for(int i=0;i<=B-1;i++){
		scanf("%lld",&a[i]);
		sum+=(i*a[i])%mod;
		sum%=mod;
	}
	if(sum!=0){
		a[sum]-=1;
	}
	for(int i=0;i<=B-1;i++){
		if(a[i]==0) continue;
		exc[wss]=i;
		lst[++tot]=wss;
		wss+=a[i];
	}
	lst[++tot]=wss;
	exc[wss]=-1;
	for(int i=tot;i>=0;i--){
		lstr[i]=lst[tot-i];
	}
	for(int i=1;i<=q;i++){
		scanf("%lld",&k);
		ll aaa=*lower_bound(lstr,lstr+tot,k,greater<ll> () );
		printf("%d\n",exc[aaa]);
	}
	return 0;
}

Thanks!

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值