【YbtOJ#752】最优分组

题目

题目链接:https://www.ybtoj.com.cn/contest/118/problem/2

(nleq 10^6)

思路

首先我们忽略 (c) 的限制,如果只考虑 (d) 的话,不难发现以第 (i) 个人结尾分组,左端点是一段连续的区间 ([lft_i,i))。 可以用 ST 表预处理区间最小值然后双指针扫一遍求出 (lft)
加入了 (c) 的限制之后,此时和每一个点能转移过来的位置就不是连续的区间了。考虑对于当前区间内 (c) 的最大值分治,也就是建出笛卡尔树然后按照深度优先遍历进行转移。
设区间 ([l,r]),最大值所在位置为 (mid),先递归处理 ([l,mid)),然后求 ([l,mid))([mid,r]) 的贡献。考虑三种情况:

  1. (lft_ileq l)(i<mid+c_{mid})
    这一段区间能被贡献的区间一定是左端点为 (l),右端点依次加一的区间。我们求出这一段区间的左端点 (=max(mid,l+c_{mid})),直接在线段树上求出能贡献到它的区间的贡献之和;然后从左端点开始往右枚举,每次增加一个位置的贡献,所以一次右移的复杂度是 (O(1)) 的。
    线段树上查询的次数为 (O(n)),所以每次第一个位置的总复杂度是 (O(nlog n))。而右移的长度最多是 ([l,mid))([mid,r]) 的较小值,所以类似每次类似一个启发式合并的过程,总复杂度依然是 (O(nlog n))
  2. (lft_ileq l)(igeq mid+c_{mid})
    这一段显然可以被 ([l,mid)) 全部贡献到,直接线段树区间查询,再区间赋值即可。
  3. (l<lft_i<mid)
    因为每一个点指挥在这样的情况一次,所以直接暴力线段树区间询问即可。

到叶子节点之后在 (f) 和线段树上取一下最优即可。
时间复杂度 (O(nlog n))

代码

#include <bits/stdc++.h>
#define mp make_pair
using namespace std;

const int N=1000010,LG=20,MOD=1e9+7;
int n,c[N],d[N],lft[N],lg[N],st[N][LG+1];

int read()
{
	int d=0; char ch=getchar();
	while (!isdigit(ch)) ch=getchar();
	while (isdigit(ch)) d=(d<<3)+(d<<1)+ch-48,ch=getchar();
	return d;
}

void buildst()
{
	for (int i=2;i<=n;i++) lg[i]=lg[i>>1]+1;
	for (int i=1;i<=n;i++) st[i][0]=i;
	for (int i=n;i>=1;i--)
		for (int j=1;i+(1<<j)-1<=n;j++)
			if (d[st[i][j-1]]<d[st[i+(1<<(j-1))][j-1]])
				st[i][j]=st[i][j-1];
			else
				st[i][j]=st[i+(1<<(j-1))][j-1];
}

int getmin(int l,int r)
{
	int k=lg[r-l+1];
	if (d[st[l][k]]<d[st[r-(1<<k)+1][k]])
		return st[l][k];
	else
		return st[r-(1<<k)+1][k];
}

struct node
{
	int v,c;
	
	friend node operator +(node x,node y)
	{
		if (!y.c) return x;
		if (!x.c) return y;
		if (x.v<y.v) return y;
		if (x.v>y.v) return x;
		return (node){x.v,(x.c+y.c)%MOD};
	} 
}f[N];

struct SegTree
{
	node val[N*4],lazy[N*4];
	
	void pushup(int x)
	{
		val[x]=val[x*2]+val[x*2+1];
	}
	
	void pushdown(int x)
	{
		if (lazy[x].c)
		{
			val[x*2]=val[x*2]+lazy[x];
			val[x*2+1]=val[x*2+1]+lazy[x];
			lazy[x*2]=lazy[x*2]+lazy[x];
			lazy[x*2+1]=lazy[x*2+1]+lazy[x];
			lazy[x]=(node){0,0};
		}
	}
	
	void update(int x,int l,int r,int ql,int qr,node v)
	{
		if (ql>qr) return;
		if (ql<=l && qr>=r)
		{
			val[x]=val[x]+v; lazy[x]=lazy[x]+v;
			return;
		}
		pushdown(x);
		int mid=(l+r)>>1;
		if (ql<=mid) update(x*2,l,mid,ql,qr,v);
		if (qr>mid) update(x*2+1,mid+1,r,ql,qr,v);
		pushup(x);
	}
	
	node query(int x,int l,int r,int ql,int qr)
	{
		if (ql>qr) return (node){0,0};
		if (ql<=l && qr>=r) return val[x];
		pushdown(x);
		int mid=(l+r)>>1; node res=(node){0,0};
		if (ql<=mid) res=res+query(x*2,l,mid,ql,qr);
		if (qr>mid) res=res+query(x*2+1,mid+1,r,ql,qr);
		return res;
	}
}seg;

void solve(int l,int r)
{
	if (l==r)
	{
		f[l]=f[l]+seg.query(1,0,n,l,l);
		f[l].v++;
		seg.update(1,0,n,l,l,f[l]);
		return;
	}
	int mid=getmin(l+1,r);
	solve(l,mid-1);
	int ll=mid,rr=r,mm,pos;
	while (ll<=rr)
	{
		mm=(ll+rr)>>1;
		if (lft[mm]<=l) ll=mm+1;
			else rr=mm-1;
	}
	pos=ll-1;
	// part 1
	node res=seg.query(1,0,n,l,max(mid-c[mid],l));
	for (int i=max(mid,l+c[mid]);i<=pos;i++)
	{
		if (mid+c[mid]<=i) break;
		f[i]=f[i]+res;
		res=res+f[i-c[mid]+1];
	}
	// part 2
	res=seg.query(1,0,n,l,mid-1);
	seg.update(1,0,n,mid+c[mid],pos,res);
	// part 3
	for (int i=pos+1;i<=r;i++)
	{
		if (lft[i]>=mid) break;
		f[i]=f[i]+seg.query(1,0,n,lft[i],min(mid-1,i-c[mid]));
	}
	solve(mid,r);
}

int main()
{
	freopen("schooldays.in","r",stdin);
	freopen("schooldays.out","w",stdout);
	n=read();
	for (int i=1;i<=n;i++)
		c[i]=read(),d[i]=read();
	buildst();
	for (int i=1,j=1;i<=n;i++)
	{
		while (i-j+1>d[getmin(j,i)]) j++;
		lft[i]=j-1;
	}
	for (int i=1;i<=n;i++) d[i]=n-c[i]+1;
	buildst();
	f[0]=(node){0,1};
	solve(0,n);
	if (f[n].c) printf("%d %d",f[n].v-1,f[n].c);
		else printf("-1");
	return 0;
}
原文地址:https://www.cnblogs.com/stoorz/p/14420225.html