[ICPC2019 南昌站] Eating Plan

题目描述:

有一个N的全排列
每个位置数的价值是他的阶乘 mod 998857459
现在给出m个询问,每次给出一个数,问最短的子段区间和大于等于这个数的时候,这个最短子段的长度是多少, 不存在请输出 -1 (子段和也要 mod 998857459)

题目分析:

首先 对于M个询问,值大的子段区间长度一定不小于值小的,即如果我们把M个询问离线,按照值升序排列,最后的答案是一个单调不降序列
由于mod是一个和数,当一个值大于这个mod的最大因子的时候,那么它的阶乘mod 998857459 一定是0 这里我为了方便取了 3000为上限
我们可以把这个序列的0去掉然后组成一个没有0的序列,这样我们就可以 3000^2的复杂度去枚举出每种组合情况,然后把这个值对于的长度放到询问里最后一个小于子段和的答案里面(这里可以二分实现)。
最后对答案取一个后缀最小值即可。
O ( 300 0 2 ∗ l o g 2 m ) O(3000^2*log_{2}m) O(30002log2m)

题目链接:

计蒜客

AC代码:

#include <iostream>
#include <cstring>
#include <algorithm>
#define int long long
const int mod=998857459;
const int maxm=1e5+100;
int ans[maxm],num[maxm],Ans[maxm];
int cnt[maxm];
int js[maxm],tot;
struct node{
	int id,val,ans;
}a[maxm];
int n,m;
inline int solve(int x)
{
	int l=1,r=m;
	int ans=0;
	while(l<=r)
	{
		int mid=(l+r)>>1;
		if(a[mid].val<=x) ans=mid,l=mid+1;
		else r=mid-1;
	}
	return ans;
}
inline bool comp(node x,node y)
{
	return x.val<y.val;
}
signed main()
{
	//freopen("1.txt","r",stdin);
	//freopen("test.out","w",stdout); 
	scanf("%lld%lld",&n,&m);
	js[0]=1;
	for(int i=1;i<=3000;i++)
	 js[i]=(js[i-1]*i*1ll)%mod;
	for(int i=1,x;i<=n;i++)
	{
		scanf("%lld",&x);
		if(x<=3000) num[++tot]=js[x],cnt[tot]=i;
	}
	for(int i=1;i<=m;i++)
	 scanf("%lld",&a[i].val),a[i].id=i,a[i].ans=0x7fffffff;
	std::sort(a+1,a+m+1,comp);
	for(int i=1;i<=tot;i++)
	{
		int sum=0;
		for(int j=i;j<=tot;j++)
		{
			sum=(sum+num[j])%mod;
			int poi=solve(sum);
			a[poi].ans=std::min(a[poi].ans,cnt[j]-cnt[i]+1);
		}
	}
	Ans[a[m].id]=a[m].ans;
	for(int i=m-1;i>=1;i--) a[i].ans=std::min(a[i+1].ans,a[i].ans),Ans[a[i].id]=a[i].ans;
	for(int i=1;i<=m;i++) printf("%lld\n",Ans[i]==0x7fffffff?-1:Ans[i]);
	return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值