博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
[PA2014]Druzyny
阅读量:7045 次
发布时间:2019-06-28

本文共 3801 字,大约阅读时间需要 12 分钟。

题目描述

体育课上,n个小朋友排成一行(从1到n编号),老师想把他们分成若干组,每一组都包含编号连续的一段小朋友,每个小朋友属于且仅属于一个组。

第i个小朋友希望它所在的组的人数不多于d[i],不少于c[i],否则他就会不满意。
在所有小朋友都满意的前提下,求可以分成的组的数目的最大值,以及有多少种分组方案能达到最大值。

题解

现在假设不考虑C的限制,那么它的转移是一段连续的区间我们可以用单调队列求出来这一段区间。

加上C的限制以后,我们不好直接处理,所以考虑分治。

假设我们当前处理的区间为[l,r]那么我们考虑取[l+1,r]Cmax作为中点,先分治左边,在计算跨越区间的贡献。

如果我们这样直接算的话复杂度不对,是T(n)=T(x)+T(n-x)+n=n^2

考虑到T(n)=T(x)+T(n-x)+min(x,n-x)=nlogn

所以我们考虑优化一下转移。

现在我们假设[l,mid-1]都算完了,一开始的转移指针在max(l+c[mid],mid)

然后我们再用一对左右指针维护当前合法的转移点,然后用线段树维护答案。

每次转移指针往后动的时候,右指针也会往右动一个,我们的答案是支持增量的,所以可以O(1)维护。

但是左指针往右动的时候,我们的答案是不支持减法的,所以还要继续在线段树上查。

当右端点不能懂得时候,我们把不同种类的左端点分别批量操作就好了。

从网上看到了一个复杂度证明:

代码

#include 
#define N 1000005using namespace std;const int mod=1000000007;int c[N],lef[N],n;inline int rd(){ int x=0;char c=getchar();bool f=0; while(!isdigit(c)){
if(c=='-')f=1;c=getchar();} while(isdigit(c)){x=(x<<1)+(x<<3)+(c^48);c=getchar();} return f?-x:x;}struct segmax{ int tr[N<<2],id[N<<2]; void build(int cnt,int l,int r){ if(l==r){tr[cnt]=c[l];id[cnt]=l;return;} int mid=(l+r)>>1; build(cnt<<1,l,mid);build(cnt<<1|1,mid+1,r); tr[cnt]=max(tr[cnt<<1],tr[cnt<<1|1]); id[cnt]=tr[cnt<<1]>=tr[cnt<<1|1]?id[cnt<<1]:id[cnt<<1|1]; } void query(int cnt,int l,int r,int L,int R,int &ans1,int &ans2){ if(l>=L&&r<=R){ if(tr[cnt]>ans1){ans1=tr[cnt];ans2=id[cnt];} return; } int mid=(l+r)>>1; if(mid>=L)query(cnt<<1,l,mid,L,R,ans1,ans2); if(mid
<<1|1,mid+1,r,L,R,ans1,ans2); }}T1;struct node{ int mx,cnt; node(int x=-1e9,int y=0){mx=x;cnt=y;} node operator +(const node &b)const{ if(mx==b.mx)return node(mx,(cnt+b.cnt)%mod); else if(mx
>1; rs[cnt]=++ct;ls[cnt]=++ct; build(ls[cnt],l,mid);build(rs[cnt],mid+1,r); tr[cnt]=tr[ls[cnt]]+tr[rs[cnt]]; } void upd(int cnt,int l,int r,int L,int R,node x){ if(l>=L&&r<=R){tr[cnt]=tr[cnt]+x;la[cnt]=la[cnt]+x;return;} int mid=(l+r)>>1; if(la[cnt].cnt)pushdown(cnt); if(mid>=L)upd(ls[cnt],l,mid,L,R,x); if(mid
R)return; if(l>=L&&r<=R){x=x+tr[cnt];return;} int mid=(l+r)>>1; if(la[cnt].cnt)pushdown(cnt); if(mid>=L)query(ls[cnt],l,mid,L,R,x); if(mid
>1; if(lef[mid]<=x)ans=mid,l=mid+1; else r=mid-1; } return ans;}inline void work(int l,int mid,int r){ int i=max(c[mid]+l,mid); int nowr=min(mid-1,i-c[mid]),nowl=max(l,lef[mid]);node ans=node(); if(i>r||lef[i]>=mid)return; T2.query(1,0,n,nowl,nowr,ans);if(ans.cnt)ans=ans+1; for(;i<=r;++i){ if(lef[i]>nowl){ if(lef[i]>=mid)return; nowl=lef[i]; ans=node(); if(nowl<=nowr)T2.query(1,0,n,nowl,nowr,ans),ans=ans+1; } f[i]=f[i]+ans; nowr++;if(nowl<=nowr)ans=ans+(f[nowr]+1); if(nowr>mid-1){nowr--;break;} } ++i; while(i<=r){ if(lef[i]>=nowl){ nowl=lef[i]; if(nowl>=mid)return; } ans=node(); T2.query(1,0,n,nowl,mid-1,ans);if(ans.cnt)ans.mx++; int t=efs(i,r,nowl); T2.upd(1,0,n,i,t,ans); i=t+1; }}void solve(int l,int r){ if(l>=r){node x=node();T2.upd(1,0,n,l,l,f[l]);T2.query(1,0,n,l,l,x);f[l]=x;return;} int mx=0,id=0; T1.query(1,0,n,l+1,r,mx,id); solve(l,id-1); work(l,id,r); solve(id,r);}inline void prework(){ int q[N],d[N]; memset(q,0,sizeof(q));memset(d,0,sizeof(d)); n=rd(); for(int i=1;i<=n;++i)c[i]=rd(),d[i]=rd(); int h=1,t=0,p=1; for(int i=1;i<=n;++i){ while(h<=t&&d[q[t]]>=d[i])t--; q[++t]=i; while(h<=t&&i-p+1>d[q[h]]){p++;while(h<=t&&q[h]

转载于:https://www.cnblogs.com/ZH-comld/p/10432772.html

你可能感兴趣的文章
flash sin
查看>>
CCF201604-3 路径解析(100分)
查看>>
HDU1879 继续畅通工程
查看>>
一步一步使用Ext JS MVC与Asp.Net MVC 3开发简单的CMS后台管理系统之调整首页显示...
查看>>
初学python之,IDLE安装
查看>>
手动创建SQL_profile 改变和稳定 SQL 执行计划
查看>>
8.1数码相框功能及程序框架
查看>>
【leetcode】155 - Min Stack
查看>>
Linux常用基本命令(笔记)
查看>>
阿姆斯特朗数
查看>>
“将偷懒进行到极致!”——EasyCode.Net代码生成器图文评测
查看>>
JavaScript 变量提升
查看>>
mysql实现递归查询
查看>>
【Django笔记二】Django2.0配置模板和静态文件
查看>>
jsonp
查看>>
元素的属性及分析
查看>>
Ajax中location.href无法跳转的解决办法
查看>>
fedora23没有/var/log/messages &如何禁用后台自动更新软件?
查看>>
Linux基础_软链接,硬链接
查看>>
Java-枚举类,注解
查看>>