• 【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;
    }
    
  • 相关阅读:
    android判断程序是否前台显示---及判断屏幕是否是亮的----附赠BAIDU定位的工具类实现代码
    Lock and synchronized 摘自官网, 摘录下来方便笔记
    SQLite 基础
    如何stop,suspend一个线程 在deprecated的情况下
    HashMap 的遍历 for:each 和EntrySet两种方法
    ListView图片异步加载与缓存策略
    android debug学习笔记 monkeyrunner
    回溯法和DFS leetcode Combination Sum
    leetcode Count and Say
    leetcode Jump Game I II 待续 贪心看不懂啊!!!!
  • 原文地址:https://www.cnblogs.com/stoorz/p/14420225.html
Copyright © 2020-2023  润新知