[LOJ]#2773. 「ROI 2017 Day 2」学习轨迹 线段树

Solution

如果只上一所学校的课,那么显然要选择这所学校的所有课程。因此,至少有一所学校选择的课程权值超过了这所学校总权值的一半。不妨强制第一所学校要超过,那么设第一所学校第一次前缀和超过总权值一半的位置为 p p p,则这个位置一定要被选择。在第二所学校选择了一些课程后,一定是从 p p p开始,尽量往两边扩展。
枚举第二所学校选择的右端点 r r r,那么每次最多会有一个位置不能再被选择。可以用两个单调栈维护当前每个 l l l到当前的 r r r的区间,他们在第一所学校选择的左右端点分别是什么,这些被影响的贡献是相同的,可以用一个线段树维护。

Code

#include
using namespace std;
#define LL long long
#define pa pair
const int Maxn=500010;
const LL inf=(1LL<<50);
int read()
{
	int x=0,f=1;char ch=getchar();
	while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}
	while(ch>='0'&&ch<='9')x=(x<<3)+(x<<1)+(ch^48),ch=getchar();
	return x*f;
}
int n,m,a[Maxn],b[Maxn],A[Maxn],B[Maxn],cnt=0;
struct P{int x,id;}T[Maxn<<1];
bool cmp(P a,P b){return a.x<b.x;}
struct Seg{int l,r,lc,rc;LL p,mx,tag;}tr[Maxn<<1];
int tot=0;
void Add(int x,LL v){tr[x].tag+=v;tr[x].mx+=v;}
void up(int x)
{
	int lc=tr[x].lc,rc=tr[x].rc;
	if(tr[lc].mx>tr[rc].mx)tr[x].mx=tr[lc].mx,tr[x].p=tr[lc].p;
	else tr[x].mx=tr[rc].mx,tr[x].p=tr[rc].p;
}
void down(int x)
{
	int lc=tr[x].lc,rc=tr[x].rc;LL t=tr[x].tag;
	if(t)tr[x].tag=0,Add(lc,t),Add(rc,t);
}
void build(int l,int r)
{
	int x=++tot;
	tr[x].l=l;tr[x].r=r;tr[x].tag=0;tr[x].mx=0;tr[x].p=l;
	if(l==r)return;
	int mid=l+r>>1;
	tr[x].lc=tot+1,build(l,mid);
	tr[x].rc=tot+1,build(mid+1,r);
}
void add(int x,int l,int r,LL v)
{
	if(tr[x].l==l&&tr[x].r==r){Add(x,v);return;}
	int mid=tr[x].l+tr[x].r>>1,lc=tr[x].lc,rc=tr[x].rc;
	down(x);
	if(r<=mid)add(lc,l,r,v);
	else if(l>mid)add(rc,l,r,v);
	else add(lc,l,mid,v),add(rc,mid+1,r,v);
	up(x);
}
int L1,R1,L2,R2;LL Ans=0;
int l1,r1,l2,r2;LL ans=0;
int pos[Maxn<<1],ul[Maxn],ur[Maxn];
struct Node
{
	int p,L,R;
	Node(int _p=0,int _L=0,int _R=0){p=_p,L=_L,R=_R;}
};
Node sta1[Maxn],sta2[Maxn];int top1,top2;LL sum[Maxn];
int fl[Maxn];int Fl(int x){return((fl[x]==x)?x:fl[x]=Fl(fl[x]));}
int fr[Maxn];int Fr(int x){return((fr[x]==x)?x:fr[x]=Fr(fr[x]));}
void solve(int op)
{
	LL s2=0;int pp;
	for(int i=1;i<=cnt;i++)pos[i]=-1;
	sum[0]=0;
	for(int i=1;i<=n;i++)sum[i]=sum[i-1]+a[i],pos[A[i]]=i;
	for(int i=1;i<=m;i++)fl[i]=fr[i]=i;
	ans=sum[n];l1=1,r1=n;l2=r2=0;
	for(int i=1;i<=n;i++)
	{
		s2+=a[i];
		if(s2>(sum[n]>>1)){pp=i;break;}
	}
	tot=0;build(1,m);top1=top2=0;
	for(int r=1;r<=m;r++)
	{
		add(1,1,r,b[r]);
		if(pos[B[r]]==-1)
		{
			ul[r]=1,ur[r]=n;add(1,r,r,sum[n]);
			sta1[++top1]=Node(1,r,r);
			sta2[++top2]=Node(n,r,r);
		}
		else
		{
			if(pos[B[r]]<pp)
			{
				int ll=r;int t=pos[B[r]]+1;
				while(top1&&sta1[top1].p<=t)
				{
					LL delta=-(sum[t-1]-sum[sta1[top1].p-1]);
					add(1,sta1[top1].L,sta1[top1].R,delta);
					ll=sta1[top1].L;fl[sta1[top1].R]=r;
					top1--;
				}
				ul[r]=t;ur[r]=n;add(1,r,r,sum[n]-sum[t-1]);
				sta1[++top1]=Node(t,ll,r);
				sta2[++top2]=Node(n,r,r);
			}
			else
			{
				int ll=r;int t=pos[B[r]]-1;
				while(top2&&sta2[top2].p>=t)
				{
					LL delta=-(sum[sta2[top2].p]-sum[t]);
					add(1,sta2[top2].L,sta2[top2].R,delta);
					ll=sta2[top2].L;fr[sta2[top2].R]=r;
					top2--;
				}
				ul[r]=1;ur[r]=t;add(1,r,r,sum[t]);
				sta2[++top2]=Node(t,ll,r);
				sta1[++top1]=Node(1,r,r);
			}
		}
		if(tr[1].mx>ans)
		{
			ans=tr[1].mx;
			l2=tr[1].p,r2=r;
			l1=ul[Fl(tr[1].p)],r1=ur[Fr(tr[1].p)];
		}
	}
	if(op)swap(l1,l2),swap(r1,r2);
	if(ans>Ans)Ans=ans,L1=l1,R1=r1,L2=l2,R2=r2;
}
int main()
{
	n=read(),m=read();
	for(int i=1;i<=n;i++)T[i].x=read(),T[i].id=i;
	for(int i=1;i<=n;i++)a[i]=read();
	for(int i=1;i<=m;i++)T[i+n].x=read(),T[i+n].id=i+n;
	for(int i=1;i<=m;i++)b[i]=read();
	sort(T+1,T+1+n+m,cmp);
	for(int i=1;i<=n+m;i++)
	{
		if(i==1||T[i].x!=T[i-1].x)cnt++;
		if(T[i].id<=n)A[T[i].id]=cnt;else B[T[i].id-n]=cnt;
	}
	solve(0);
	for(int i=1;i<=max(n,m);i++)swap(a[i],b[i]),swap(A[i],B[i]);
	swap(n,m);
	solve(1);
	if(L1>R1)L1=R1=0;if(L2>R2)L2=R2=0;
	printf("%lld\n%d %d\n%d %d",Ans,L1,R1,L2,R2);
}

你可能感兴趣的:(线段树)