[PA2014]Druzyny

题目描述

体育课上,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)维护。

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

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

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

[PA2014]Druzyny

代码

#include <bits/stdc++.h>
#define N 1000005
using namespace std;
const int mod=;
int c[N],lef[N],n;
inline int rd(){
int x=;char c=getchar();bool f=;
while(!isdigit(c)){if(c=='-')f=;c=getchar();}
while(isdigit(c)){x=(x<<)+(x<<)+(c^);c=getchar();}
return f?-x:x;
}
struct segmax{
int tr[N<<],id[N<<];
void build(int cnt,int l,int r){
if(l==r){tr[cnt]=c[l];id[cnt]=l;return;}
int mid=(l+r)>>;
build(cnt<<,l,mid);build(cnt<<|,mid+,r);
tr[cnt]=max(tr[cnt<<],tr[cnt<<|]);
id[cnt]=tr[cnt<<]>=tr[cnt<<|]?id[cnt<<]:id[cnt<<|];
}
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)>>;
if(mid>=L)query(cnt<<,l,mid,L,R,ans1,ans2);
if(mid<R)query(cnt<<|,mid+,r,L,R,ans1,ans2);
}
}T1;
struct node{
int mx,cnt;
node(int x=-1e9,int y=){mx=x;cnt=y;}
node operator +(const node &b)const{
if(mx==b.mx)return node(mx,(cnt+b.cnt)%mod);
else if(mx<b.mx)return b;else return *this;
}
node operator +(const int &b)const{return node(mx+b,cnt);}
}f[N];
struct segsum{
node tr[N<<],la[N<<];int ls[N<<],rs[N<<],ct;
segsum(){ct=;}
inline void pushdown(int cnt){
tr[ls[cnt]]=tr[ls[cnt]]+la[cnt];
tr[rs[cnt]]=tr[rs[cnt]]+la[cnt];
la[ls[cnt]]=la[ls[cnt]]+la[cnt];
la[rs[cnt]]=la[rs[cnt]]+la[cnt];
la[cnt]=node();
}
void build(int cnt,int l,int r){
if(l==r){if(l)tr[cnt]=node(-1e9,);else tr[cnt]=node(,);return;}
int mid=(l+r)>>;
rs[cnt]=++ct;ls[cnt]=++ct;
build(ls[cnt],l,mid);build(rs[cnt],mid+,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)>>;
if(la[cnt].cnt)pushdown(cnt);
if(mid>=L)upd(ls[cnt],l,mid,L,R,x);
if(mid<R)upd(rs[cnt],mid+,r,L,R,x);
tr[cnt]=tr[ls[cnt]]+tr[rs[cnt]];
}
void query(int cnt,int l,int r,int L,int R,node &x){
if(L>R)return;
if(l>=L&&r<=R){x=x+tr[cnt];return;}
int mid=(l+r)>>;
if(la[cnt].cnt)pushdown(cnt);
if(mid>=L)query(ls[cnt],l,mid,L,R,x);
if(mid<R)query(rs[cnt],mid+,r,L,R,x);
}
}T2;
inline int efs(int l,int r,int x){
int ans=;
while(l<=r){
int mid=(l+r)>>;
if(lef[mid]<=x)ans=mid,l=mid+;
else r=mid-;
}
return ans;
}
inline void work(int l,int mid,int r){
int i=max(c[mid]+l,mid);
int nowr=min(mid-,i-c[mid]),nowl=max(l,lef[mid]);node ans=node();
if(i>r||lef[i]>=mid)return;
T2.query(,,n,nowl,nowr,ans);if(ans.cnt)ans=ans+;
for(;i<=r;++i){
if(lef[i]>nowl){
if(lef[i]>=mid)return;
nowl=lef[i];
ans=node();
if(nowl<=nowr)T2.query(,,n,nowl,nowr,ans),ans=ans+;
}
f[i]=f[i]+ans;
nowr++;if(nowl<=nowr)ans=ans+(f[nowr]+);
if(nowr>mid-){nowr--;break;}
}
++i;
while(i<=r){
if(lef[i]>=nowl){
nowl=lef[i];
if(nowl>=mid)return;
}
ans=node();
T2.query(,,n,nowl,mid-,ans);if(ans.cnt)ans.mx++;
int t=efs(i,r,nowl);
T2.upd(,,n,i,t,ans);
i=t+;
}
}
void solve(int l,int r){
if(l>=r){node x=node();T2.upd(,,n,l,l,f[l]);T2.query(,,n,l,l,x);f[l]=x;return;}
int mx=,id=;
T1.query(,,n,l+,r,mx,id);
solve(l,id-);
work(l,id,r);
solve(id,r);
}
inline void prework(){
int q[N],d[N];
memset(q,,sizeof(q));memset(d,,sizeof(d));
n=rd();
for(int i=;i<=n;++i)c[i]=rd(),d[i]=rd();
int h=,t=,p=;
for(int i=;i<=n;++i){
while(h<=t&&d[q[t]]>=d[i])t--;
q[++t]=i;
while(h<=t&&i-p+>d[q[h]]){p++;while(h<=t&&q[h]<p)h++;}
lef[i]=p-;
}
}
int main(){
prework();
T1.build(,,n);
T2.build(,,n);
solve(,n);
node ans;
T2.query(,,n,n,n,ans);
if(!ans.cnt)puts("NIE");
else printf("%d %d",ans.mx,ans.cnt);
return ;
}
上一篇:selenium+python笔记9


下一篇:POJ1061青蛙的约会[扩展欧几里得]