【LOJ6515】贪玩蓝月

题目大意

  有一个双端队列,每个元素是一个物品,每个物品有体积和价值两个属性。

  有 (n) 个操作,分为 (5) 种:前后端插入删除,还有询问:选出一些物品,满足这些物品的体积之和模 (p)([l,r]) 之间,问你价值和最大是多少。

  (nleq 50000,pleq 500)

题解

离线做法

  处理出每个物品加入队列的时间和删除的时间,插入到线段树里面分治就好了。

  时间复杂度:(O(nplog n))

在线做法

  考虑对于一个队列,从中间向两边各建一个可持久化栈维护两边中间到两边的物品加在一起的信息。

  如果删除后把某一个栈的栈底删掉了,就从中间开始重构这两个栈。

  对于一个询问,枚举左边的物品体积是多少,然后右边的物品体积就是一个区间。

  可以按照 (r-l+1) 分块,这样每次询问就是块内的一个前缀和一个后缀。

  时间复杂度:(O(np))

代码

#include<cstdio>
#include<cstring>
#include<algorithm>
#include<cstdlib>
#include<ctime>
#include<utility>
#include<functional>
#include<cmath>
#include<vector>
//using namespace std;
using std::min;
using std::max;
using std::swap;
using std::sort;
using std::reverse;
using std::random_shuffle;
using std::lower_bound;
using std::upper_bound;
using std::unique;
using std::vector;
typedef long long ll;
typedef unsigned long long ull;
typedef double db;
typedef std::pair<int,int> pii;
typedef std::pair<ll,ll> pll;
void open(const char *s){
#ifndef ONLINE_JUDGE
	char str[100];sprintf(str,"%s.in",s);freopen(str,"r",stdin);sprintf(str,"%s.out",s);freopen(str,"w",stdout);
#endif
}
void open2(const char *s){
#ifdef DEBUG
	char str[100];sprintf(str,"%s.in",s);freopen(str,"r",stdin);sprintf(str,"%s.out",s);freopen(str,"w",stdout);
#endif
}
int rd(){int s=0,c,b=0;while(((c=getchar())<'0'||c>'9')&&c!='-');if(c=='-'){c=getchar();b=1;}do{s=s*10+c-'0';}while((c=getchar())>='0'&&c<='9');return b?-s:s;}
void put(int x){if(!x){putchar('0');return;}static int c[20];int t=0;while(x){c[++t]=x%10;x/=10;}while(t)putchar(c[t--]+'0');}
int upmin(int &a,int b){if(b<a){a=b;return 1;}return 0;}
int upmax(int &a,int b){if(b>a){a=b;return 1;}return 0;}
const int N=50010;
const int P=510;
const ll inf=0x3fffffffffffffffll;
struct info
{
	ll a[P];
	ll &operator [](int x)
	{
		return a[x];
	}
	void clear()
	{
		memset(a,0xc0,sizeof a);
		a[0]=0;
	}
};
int n,p;
info a1[N],a2[N];
int l,r,mid;
int t1,t2;
int w[2*N],v[2*N];
void init()
{
	l=50001;
	r=50000;
	mid=50000;
	a1[0].clear();
	a2[0].clear();
}
void addl(int x)
{
	t1++;
	a1[t1]=a1[t1-1];
	for(int i=0;i<p;i++)
		a1[t1][(i+w[x])%p]=max(a1[t1][(i+w[x])%p],a1[t1-1][i]+v[x]);
}
void addr(int x)
{
	t2++;
	a2[t2]=a2[t2-1];
	for(int i=0;i<p;i++)
		a2[t2][(i+w[x])%p]=max(a2[t2][(i+w[x])%p],a2[t2-1][i]+v[x]);
}
void rebuild()
{
	mid=(l+r)>>1;
	t1=0;
	t2=0;
	for(int i=mid;i>=l;i--)
		addl(i);
	for(int i=mid+1;i<=r;i++)
		addr(i);
}
int pre[P],suf[P];
void query()
{
	int ql,qr;
	scanf("%d%d",&ql,&qr);
	int len=qr-ql+1;
	ll s=-inf;
	for(int i=0;i<p;i++)
	{
		if(i%len==0)
			s=-inf;
		s=max(s,a1[t1][i]);
		pre[i]=s;
	}
	s=-inf;
	for(int i=p-1;i>=0;i--)
	{
		if(i%len==len-1)
			s=-inf;
		s=max(s,a1[t1][i]);
		suf[i]=s;
	}
	ll ans=-1;
//	for(int i=0;i<p;i++)
//		for(int j=0;j<p;j++)
//			if((i+j)%p>=ql&&(i+j)%p<=qr)
//			{
//				ans=max(ans,a1[t1][i]+a2[t2][j]);
//				if(ans==481594)
//					int xxx=1;
//			}
	for(int i=0;i<p;i++)
	{
		int _l=ql-i;
		int _r=qr-i;
		if(_l<0)
			_l+=p;
		if(_r<0)
			_r+=p;
		ans=max(ans,a2[t2][i]+max(pre[_r],suf[_l]));
		int z=p/len*len;
		if(_l>_r&&_l<z)
			ans=max(ans,a2[t2][i]+suf[z]);
	}
	printf("%lld
",ans);
}
char op[10];
void gao()
{
	scanf("%s",op);
	if(op[0]=='I')
	{
		if(op[1]=='F')
		{
			l--;
			scanf("%d%d",&w[l],&v[l]);
			w[l]%=p;
			addl(l);
		}
		else
		{
			r++;
			scanf("%d%d",&w[r],&v[r]);
			w[r]%=p;
			addr(r);
		}
	}
	else if(op[0]=='D')
	{
		if(op[1]=='F')
		{
			l++;
			t1--;
			if(l>mid+1)
				rebuild();
		}
		else
		{
			r--;
			t2--;
			if(r<=mid+1)
				rebuild();
		}
	}
	else
	{
		query();
	}
}
int main()
{
	open("loj6515");
	rd();
	scanf("%d%d",&n,&p);
	init();
	for(int i=1;i<=n;i++)
	{
		if(i==75)
			int xxx=1;
		gao();
//		if(op[0]=='Q')
//			printf("%d
",i);
	}
	return 0;
}
原文地址:https://www.cnblogs.com/ywwyww/p/9846430.html