题目描述

体育课上,n个小朋友排成一行(从1到n编号),老师想把他们分成若干组,每一组都包含编号连续的一段小朋友,每个小朋友属于且仅属于一个组。
第i个小朋友希望它所在的组的人数不多于d[i],不少于c[i],否则他就会不满意。
在所有小朋友都满意的前提下,求可以分成的组的数目的最大值,以及有多少种分组方案能达到最大值。

神题

我们考虑DP。
f[i]表示将前i个人分组的最大值,-1表示不可能,g[i]来表示最大化分组情况下的方案数。
因为g的转移和f类似,我们接下来只考虑f。
首先观察d的限制,可以通过预处理left[i]表示所有 left[i]<=j<i left[i]均满足j+1~i的d最小值不比i-j+1小。那么只有这些j有可能可以转移到i。
这个left显然有单调性,于是预处理left的时候,只需要一个单调队列来资瓷尾加头删的最小值维护即可。
接下来我们来考虑c,发现并不是很好考虑?
于是我们来考虑分治,根据c来分治。
这样可以在每一次转移明确c的最大值。
具体的,我们考虑现在确定[l,r]的f值,记为过程solve(l,r)。
我们先找到[l+1,r]中c的最大值所在位置k。
然后我们考虑用[l,k-1]的状态转移到[k,r]。
[l,k-1]的状态可以先通过solve(l,k-1)确定。
这个转移完成后可以继续调用solve(k,r)。
于是我们来解决这个转移。
我们来考虑[max(k,l+c),min(r,k-1+c)]的i。
显然只有这个区间的i能被[l,k-1]转移到。
分类讨论:
1、 left[i]<l left[i]且i<=k-1+c。
满足条件的i是连在一起的,而且i+1相对于i来说,只不过多了一个i+1-c。
因此对于第一个i用线段树查询后,接下来的都可以O(1)更新。
2、 left[i]<l left[i]且i>k-1+c。
这个时候每个i的合法范围都是[l,k-1]。
二分出满足条件的i的区间[ll,rr]。
用线段树查询出[l,k-1]的答案,然后打标记给[ll,rr]。
3、 l<=left[i]<k l。
这个时候因为left[i]不确定,对于这样的每个i我们只能分别求解,在线段树对应区间求出答案。
4、left[i]>=k。
那就没有[l,k-1]的可以转移啦,退出吧!
接下来分析一下复杂度。
先看1,首先要一个log n,接下来与扫过的i个数有关。
假设满足条件的最小i是k,最大i是k-1+c或r,我们直接当做最大i是r一定不比真实最大i小。
此时扫过的i个数是[k,r]的区间长度。
假设满足条件的最小i是l+c,最大i是k-1+c或r,我们直接当做最大i是k-1+c一定不比真实最大i小。
此时扫过的i个数是[l,k-1]的区间长度。
那么到底是多少呢?我们发现最小i是k与l+c取max,那么扫过的i个数应当是两者的较小值。
所以我们得到式子T(n)=T(x)+T(n-x)+log n+min(x,n-x)。
考虑到log n的影响只有n个,这部分是n log n的。
后面那个可以理解为启发式合并,于是T(n)=n log n。
接下来看2,显然每次只需要一个log n,分治结构有n个部分,那么就是n log n。
接下来看3,对于每个i均需要一个log n,但由于每个i显然只会被这样搞一次(分治结构中不同的部分要么left取值范围不相交要么被搞的i范围不相交),所以也是n log n。
然后这题就是n log n的!
之后当分治到l=r时,就可以在线段树中查出f和g。
实现详见代码。

#include<cstdio>
#include<algorithm>
#define fo(i,a,b) for(i=a;i<=b;i++)
#define fd(i,a,b) for(i=a;i>=b;i--)
using namespace std;
const int maxn=1000000+10,mo=1000000007;
struct dong{int c,f,g,ff,gg;
} tree[maxn*4];
int f[maxn],g[maxn],c[maxn],d[maxn],dl[maxn],left[maxn];
int i,j,k,l,t,n,m,head,tail;
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*10+ch-'0';ch=getchar();}return x*f;
}
void cmp(int &f,int &g,int ff,int gg){if (ff>f) f=ff,g=gg;else if (f==ff) g=(g+gg)%mo;
}
void mark(int p,int ff,int gg){cmp(tree[p].f,tree[p].g,ff,gg);cmp(tree[p].ff,tree[p].gg,ff,gg);
}
void down(int p){if (tree[p].ff>-1){mark(p*2,tree[p].ff,tree[p].gg);mark(p*2+1,tree[p].ff,tree[p].gg);tree[p].ff=-1;tree[p].gg=0;}
}
void update(int p){int j=tree[p*2].c,k=tree[p*2+1].c;if (c[j]>c[k]) tree[p].c=j;else tree[p].c=k;j=tree[p*2].f;k=tree[p*2+1].f;if (j>=k){tree[p].f=j;tree[p].g=tree[p*2].g;if (j==k) (tree[p].g+=tree[p*2+1].g)%=mo;}else{tree[p].f=k;tree[p].g=tree[p*2+1].g;}
}
void build(int p,int l,int r){tree[p].ff=-1;tree[p].gg=0;if (l==r){/*if (l==0) tree[p].f=0,tree[p].g=1;else */tree[p].c=l,tree[p].f=-1,tree[p].g=0;return;}int mid=(l+r)/2;build(p*2,l,mid);build(p*2+1,mid+1,r);update(p);
}
int find(int p,int l,int r,int a,int b){if (l==a&&r==b) return tree[p].c;down(p);int mid=(l+r)/2;if (b<=mid) return find(p*2,l,mid,a,b);else if (a>mid) return find(p*2+1,mid+1,r,a,b);else{int j=find(p*2,l,mid,a,mid),k=find(p*2+1,mid+1,r,mid+1,b);if (c[j]>c[k]) return j;else return k;}
}
void change(int p,int l,int r,int a,int b,int ff,int gg){if (l==a&&r==b){mark(p,ff,gg);return;}down(p);int mid=(l+r)/2;if (b<=mid) change(p*2,l,mid,a,b,ff,gg);else if (a>mid) change(p*2+1,mid+1,r,a,b,ff,gg);else change(p*2,l,mid,a,mid,ff,gg),change(p*2+1,mid+1,r,mid+1,b,ff,gg);update(p);
}
void query(int p,int l,int r,int a,int b,int &f,int &g){if (a>b){f=-1;g=0;return;}if (l==a&&r==b){f=tree[p].f;g=tree[p].g;return;}down(p);int mid=(l+r)/2;if (b<=mid) query(p*2,l,mid,a,b,f,g);else if (a>mid) query(p*2+1,mid+1,r,a,b,f,g);else{int j,k;query(p*2,l,mid,a,mid,j,k);query(p*2+1,mid+1,r,mid+1,b,f,g);if (j>f) f=j,g=k;else if (j==f) g=(g+k)%mo;}
}
int get(){while (head<=tail&&dl[head]<j+1) head++;return d[dl[head]];
}
void prepare(){head=1;tail=0;j=0;fo(i,1,n){while (head<=tail&&d[dl[tail]]>=d[i]) tail--;dl[++tail]=i;while (i-get()>j) j++;left[i]=j;}
}
int binary(int l,int r,int x){int mid;while (l<r){mid=(l+r+1)/2;if (left[mid]<x) l=mid;else r=mid-1;}return l;
}
void trans(int l,int r,int k,int c){int st=max(k,l+c),ed=min(r,k-1+c),ff=-1,gg=0,i=st;while (i<=ed){if (left[i]>=k) return;if (left[i]>=l) break;if (i==st) query(1,0,n,l,i-c,ff,gg);else cmp(ff,gg,f[i-c],g[i-c]);if (ff!=-1) cmp(f[i],g[i],ff+1,gg);i++;}if (i>r) return;if (left[i]<l){int j=binary(i,r,l);query(1,0,n,l,k-1,ff,gg);if (ff!=-1) change(1,0,n,i,j,ff+1,gg);i=j+1;}while (i<=r){if (left[i]>=k) return;query(1,0,n,left[i],min(k-1,i-c),ff,gg);if (ff!=-1) cmp(f[i],g[i],ff+1,gg);i++;}
}
void solve(int l,int r){if (l>r) return;if (l==r){change(1,0,n,l,l,f[l],g[l]);query(1,0,n,l,l,f[l],g[l]);return;}int k=find(1,0,n,l+1,r);solve(l,k-1);trans(l,r,k,c[k]);solve(k,r);
}
int main(){//freopen("data.in","r",stdin);n=read();fo(i,1,n) c[i]=read(),d[i]=read();build(1,0,n);fo(i,1,n) f[i]=-1,g[i]=0;g[0]=1; prepare();solve(0,n);if (f[n]<=0) printf("NIE\n");else printf("%d %d\n",f[n],g[n]);
}

[bzoj3711]Druzyny相关推荐

  1. BZOJ3711: [PA2014]Druzyny

    orz 考虑dp,f[i]表示1~i至多分成几段,g[i]表示1~i分成f[i]段的方案数 转移的时候将c[i],d[i]的限制分开考虑 对于d[i]的限制,不难发现他有单调性,可以预处理L[i]表示 ...

  2. BZOJ3711 PA2014Druzyny(动态规划+cdq分治+线段树)

    显然可以dp:设f[i]为前i个人最多能分多少组,则f[i]=max{f[j]}+1 (cmax<=i-j<=dmin). 容易发现d的限制是一段连续区间,二分或者随便怎么搞都行.c则有点 ...

  3. 【BZOJ 3711】[PA2014] Druzyny

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

  4. 省选之前的未完成的计划(截至到省选)

    PLAN OF THE COMING HEOI good problems: -bzoj4823:[Cqoi2017]老C的方块 [*] -bzoj3171:[Tjoi2013]循环格 [*] -bz ...

  5. python怎么调用navicat_Navicat使用与python操作数据库

    一.Navicat使用 1.测试+链接数据库,新建库 2.新建表,新增字段+类型+约束 3.设计表:外键 4.新建查询 5.建立表模型 二.python操作数据库 pymysql使用步骤: 1.与数据 ...

  6. qt Android 按键事件,QT无窗口状态下对键盘事件的监听

    Question:最近在搞linux下的一个客户端项目,需要接收键盘事件,但是又不能有界面,这种情况怎么处理呢? int main(int argc, char *argv[]) { QApplica ...

最新文章

  1. 区块链BaaS云服务(16)天德链TDBC“金丝猴链”
  2. 7.12-7.19 id、w、who、last、lastb、lastlog
  3. python网站模板下载_Python画图模板大全:从此画图不用愁
  4. mysql数据库基础简介_MySQL数据库之MySQL 基础之MySQL简介
  5. cmd 顺序启动服务_未能连接一个Windows服务 怎么回事
  6. 软件测试功能测试全套常见面试题【开放性思维题】面试总结4-3
  7. 一文带你理解URI 和 URL 有什么区别?
  8. 计算机真有趣作文,这真有趣作文(共6篇)
  9. 云适配签约中远海运特运 构建移动BPM平台
  10. 安卓移动办公软件_尚朋高科TeeTek云端移动办公系统,云端软件5G时代的趋势
  11. Simulink学习——弹球仿真三维动画模型(Simulink3D演示动画学习01)
  12. centos镜像中Everything, GnomeLive ,KdeLive ,livecd ,NetInstall的意思
  13. 科研实习 | 中科院软件所郑权老师组招收图形学方向研究实习生
  14. 大数据产品推荐:星环人工智能平台
  15. DISM命令常见用法
  16. Mac下 讲iphone备份到移动硬盘下
  17. 【开发技巧】-- 一篇女朋友也能看懂的Spring整合第三方支付(微信支付-扫码支付实现篇)
  18. stm32 hal 库组织分析
  19. 如果你想来金融科技公司……
  20. 020_截图工具未在当前计算机上运行解决方法

热门文章

  1. 微控制器和微处理器市场持续增长
  2. 战网客户端 linux,暴雪战网客户端 | Blizzard Battle.net
  3. 网站备案:阿里云-信息系统安全等级保护备案证明及网站服务合同下载
  4. Neural Collaborative Filtering(NCF)(a improvement to MF)
  5. SEC官员:ICO指南即将发布
  6. Python在线聊天室
  7. NormalBlend
  8. 程序员的诗和唐寅的诗
  9. 计算机工程实践 课程大纲,《计算机专业》实习教学大纲.doc
  10. 【ZGC】为什么初始标记需要STW(stop the world) ?