[bzoj3711]Druzyny
来源:互联网 发布:大数据产业规模 编辑:程序博客网 时间:2024/06/15 05:03
题目描述
体育课上,n个小朋友排成一行(从1到n编号),老师想把他们分成若干组,每一组都包含编号连续的一段小朋友,每个小朋友属于且仅属于一个组。
第i个小朋友希望它所在的组的人数不多于d[i],不少于c[i],否则他就会不满意。
在所有小朋友都满意的前提下,求可以分成的组的数目的最大值,以及有多少种分组方案能达到最大值。
神题
我们考虑DP。
f[i]表示将前i个人分组的最大值,-1表示不可能,g[i]来表示最大化分组情况下的方案数。
因为g的转移和f类似,我们接下来只考虑f。
首先观察d的限制,可以通过预处理left[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、
满足条件的i是连在一起的,而且i+1相对于i来说,只不过多了一个i+1-c。
因此对于第一个i用线段树查询后,接下来的都可以O(1)更新。
2、
这个时候每个i的合法范围都是[l,k-1]。
二分出满足条件的i的区间[ll,rr]。
用线段树查询出[l,k-1]的答案,然后打标记给[ll,rr]。
3、
这个时候因为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
- 【PA2014】【BZOJ3711】Druzyny
- BZOJ3711: [PA2014]Druzyny
- bzoj3711
- BZOJ 3711 [PA2014]Druzyny
- SpringMVC interceptor有时候配置的时候path="/**" 两个星号什么意思,与path="/"以及path="/*"什么区别
- Spring 校验(validator,JSR-303)简单实现
- Wine:我可以让比特币病毒感染Linux系统
- Win7下Mybatis Plugin插件安装破解及使用
- FASHION DNA: STRUCTURAL FEATURE MAPPING IN THE WORLD OF RETAIL
- [bzoj3711]Druzyny
- Android 性能优化之内存优化
- 【Android手势冲突】彻底解决RecyclerView与ScrollView滑动冲突问题,并实现RecyclerView悬停导航栏
- Fragment相关知识点学习
- java中导入、导出excel
- [Canvas]1-1Hello canvas
- Android 长按识别图中二维码 zxing
- Hibernate泛型Dao(依赖spring)
- Nginx配置详解