【OJ2216】小奇的数列

题目大意 : 给定一个长度为 \(n\) 的数列,以及 \(m\) 次询问,每次给出三个数 \(l\)\(r\) 和 \(P\),询问 \((\sum_{i=l_1}^{r_1}a_i)\;mod\;P\) 的最小值。 其中 \(l \le l_1 \le r_1 \le r\)\((n\le 5\times 10^5, m\le 10^4,P\le 500)\)

Tag: 抽屉原理、STL

最朴素的做法当然是计算前缀和再枚举 \(l_1\)\(r_1\) 。这里有一个优化:如果区间中有超过 \(P\) 个元素,那么可以根据抽屉原理证明一定存在一个区间使得区间和膜 \(P\)\(0\) ,可以据此让区间大小大于 \(P\) 的区间直接输出 \(0\)

那么我们只考虑枚举 \(l_1\)\(r_1\) 中的一个是否可以完成本题?我们可以只枚举 \(r_1\) ,显然我们要找到一个 \(l_1\) ,使得 \(s[r_1]-s[l_1]\) 最小,即 \(s[l_1]\)\(s[r_1]\) 最接近,用STL的 \(\text Set\) 即可维护。(这里 \(s[x]\) 表示 \((sum[x]-sum[l-1])\,mod\,P\)

#include<cstdio>
#include<algorithm>
#include<set>
using namespace std;
inline int _read()
{
	char c; int x=0;
	for(;c<'0'||c>'9';c=getchar());
	for(;c>='0'&&c<='9';c=getchar())x=(x<<1)+(x<<3)+c-'0';
	return x;
}
typedef long long ll;
const int N=500005;
int a[N];
ll sum[N];
int main()
{
	int n=_read(),m=_read();
	for(int i=1;i<=n;i++)
	{
		a[i]=_read();
		sum[i]=sum[i-1]+a[i];
	}
	while(m--)
	{
		int l=_read(),r=_read(),p=_read(),ans=501;
		set<ll> s;
		s.insert(0);
		if(r-l+1>=p)
		{
			puts("0");
			continue;
		}
		for(int i=l;i<=r;i++)
		{
			ll w=(sum[i]-sum[l-1])%p;
			auto it=s.upper_bound(w); if(it!=s.begin())--it;
			ans=min(ans,int((w-*it)%p));
			s.insert(w);
		}	
		printf("%d\n",ans);
	}
}
posted @ 2018-08-01 22:58  x_faraway_x  阅读(346)  评论(0)    收藏  举报