题意:
体育课上,n个小朋友排成一行(从1到n编号),老师想把他们分成若干组,每一组都包含编号连续的一段小朋友,每个小朋友属于且仅属于一个组。
第i个小朋友希望它所在的组的人数不多于d[i],不少于c[i],否则他就会不满意。
在所有小朋友都满意的前提下,求可以分成的组的数目的最大值,以及有多少种分组方案能达到最大值。
题解:
模拟赛上遇到的。。这道题思路不错。
设
Si
为
i
的合法决策集合。
可以想到朴素的
mx[i]=maxj∈Si{mx[j]}+1
dp[x]=∑j∈S,mx[j]=max{mx[k]},k∈Sdp[j]
发现带着
S
的限制想要优化这个
考虑
j∈Si
集合需要满足的条件:
发现如果只考虑
d
的限制,那么
当然如果加上
c
的限制,这个区间内的一些地方就被删除而成为了零散的几部分,然后分治的思路就是,我们枚举当前最大
但是还有一个问题是,如果单纯的这样递归下去,复杂度
并没有实质性的优化,这时候要考虑这个特殊的限制
c
要怎么用上,若当前分治中心为
1.找出第一个可行位置
p
(使得中心点满足条件)。
2.初始决策区间的
3.假设左指针一直处于左区间开头,右指针每次暴力挪动1位,那么更新状态为
O(1)
。挪到左区间末尾停止,二分可以更新的状态的区域并在线段树上更新,时间复杂度
O(logn)
。
4.暴力查找
left[i]
在区间中的位置,每个
i
只会查询一次,总体复杂度为
比较有疑问的是第3个暴力为什么是 nlogn 而不是 n2 ,这是因为每次暴力挪动右指针,次数不会超过左区间的长度,而枚举 i 不会超过右区间长度,所以这部分的复杂度为
类似启发式合并的复杂度分析,总时间为 O(nlogn)
(哦对了这道题卡空间)
#include<bits/stdc++.h>
using namespace std;
const int R_LEN=(1<<18)|1;
char ibuf[R_LEN],*sb,*tb;
inline char getc(){
(sb==tb) && (tb=(sb=ibuf)+fread(ibuf,1,R_LEN,stdin));
return (sb==tb) ? -1: *sb++;
}
inline int rd(){
char ch=getc(); int i=0,f=1;
while(!isdigit(ch)) {if(ch=='-')f=-1; ch=getc();}
while(isdigit(ch)) {i=(i<<1)+(i<<3)+ch-'0'; ch=getc();}
return i*f;
}
const int Mod=1e9+7;
const int N=1e6+50,M=1e5;
const int INF=0x3f3f3f3f;
int n,c[N],d[N],lft[N];
int mnd[N*2+M];
inline void build_d(int k,int l,int r){
if(l==r){mnd[k]=d[l]; return;}
int mid=(l+r)>>1;
build_d(k<<1,l,mid); build_d(k<<1|1,mid+1,r);
mnd[k]=min(mnd[k<<1],mnd[k<<1|1]);
}
inline int query_d(int k,int l,int r,int L,int R){
if(L<=l&&r<=R) return mnd[k];
int mid=(l+r)>>1;
if(R<=mid) return query_d(k<<1,l,mid,L,R);
else if(L>mid) return query_d(k<<1|1,mid+1,r,L,R);
else return min( query_d(k<<1,l,mid,L,R), query_d(k<<1|1,mid+1,r,L,R));
}
int mxc[N*2+M],pos_c[N*2+M];
inline void build_c(int k,int l,int r){
if(l==r) {
mxc[k]=c[l]; pos_c[k]=l; return;
}int mid=(l+r)>>1;
build_c(k<<1,l,mid); build_c(k<<1|1,mid+1,r);
(mxc[k<<1]>=mxc[k<<1|1])? (mxc[k]=mxc[k<<1],pos_c[k]=pos_c[k<<1]) :(mxc[k]=mxc[k<<1|1],pos_c[k]=pos_c[k<<1|1]);
}
typedef pair<int,int> pii;
inline pii querymx(int k,int l,int r,int L,int R){
if(L<=l&&r<=R) return make_pair(mxc[k],pos_c[k]);
int mid=(l+r)>>1;
if(R<=mid) return querymx(k<<1,l,mid,L,R);
else if(L>mid) return querymx(k<<1|1,mid+1,r,L,R);
else{
pii tr1=querymx(k<<1,l,mid,L,R),tr2=querymx(k<<1|1,mid+1,r,L,R);
return (tr1.first<tr2.first)?tr2:tr1;
}
}
struct data{
int mx,val;
data(){}
data(int mx,int val):mx(mx),val(val){}
friend inline data operator +(const data &a,const data &b){
if(a.mx!=b.mx) return (a.mx>b.mx) ?a:b;
return data(a.mx,(a.val+b.val)%Mod);
}
};
data mxf[N*2+M],f[N],tag[N*2+M];
inline void build_f(int k,int l,int r){
if(l==r) {
mxf[k]=f[l]; tag[k]=data(-INF,0); return;
}int mid=(l+r)>>1;
build_f(k<<1,l,mid); build_f(k<<1|1,mid+1,r);
mxf[k]=(mxf[k<<1]+mxf[k<<1|1]);
tag[k]=(tag[k<<1]+tag[k<<1|1]);
}
inline void modify_cov(int k,int l,int r,int pos,data val){
if(l==r) {
mxf[k]=val; return;
}int mid=(l+r)>>1;
(pos<=mid)? (modify_cov(k<<1,l,mid,pos,val)): (modify_cov(k<<1|1,mid+1,r,pos,val));
mxf[k]=(mxf[k<<1]+mxf[k<<1|1]);
}
inline void modify_add(int k,int l,int r,int L,int R,data val){
if(L<=l&&r<=R){
tag[k]=tag[k]+val;
return;
}int mid=(l+r)>>1;
if(R<=mid) modify_add(k<<1,l,mid,L,R,val);
else if(L>mid) modify_add(k<<1|1,mid+1,r,L,R,val);
else modify_add(k<<1,l,mid,L,R,val),modify_add(k<<1|1,mid+1,r,L,R,val);
}
inline data askc(int k,int l,int r,int pos){
data tp(-INF,0);
while(1){
tp=tp+tag[k];
if(l==r)break;
int mid=(l+r)>>1;
(pos<=mid) ?(k=k<<1,r=mid):(k=(k<<1)|1,l=mid+1);
}
return tp;
}
inline data qryc(int k,int l,int r,int L,int R){
if(L>R) return data(-INF,0);
if(L<=l&&r<=R) return mxf[k];
int mid=(l+r)>>1;
if(R<=mid) return qryc(k<<1,l,mid,L,R);
else if(L>mid) return qryc(k<<1|1,mid+1,r,L,R);
else return qryc(k<<1,l,mid,L,R)+qryc(k<<1|1,mid+1,r,L,R);
}
inline int srh(int l,int r,int lim){
int ans;
while(l<=r){
int mid=(l+r)>>1;
if(lft[mid]<=lim) ans=mid,l=mid+1;
else r=mid-1;
}
return ans;
}
inline data upt(const data &t) {return data(t.mx+1,t.val);}
inline void upt(int l,int mid,int r){
int p=max(mid,c[mid]+l),nowl=max(l,lft[p]),nowr=min(mid-1,p-c[mid]);
if(p>r||nowl>=mid) return;
data tp=qryc(1,0,n,nowl,nowr);
while(p<=r&&nowl<=l){
if(nowr>=mid-1){
int R=srh(p,r,l);
modify_add(1,0,n,p,R,upt(tp));
p=R+1; break;
}
f[p]=f[p]+upt(tp); tp=tp+(f[++nowr]);
nowl=max(nowl,lft[++p]);
}
for(;p<=r;){
nowl=lft[p]; nowr=min(p-c[mid],mid-1);
if(nowl>mid) return;
f[p]=f[p]+upt(qryc(1,0,n,nowl,nowr));
++p;
}
}
inline void solve(int l,int r){
if(l>r) return;
if(l==r){
modify_cov(1,0,n,l,f[l]=(f[l]+askc(1,0,n,l)));
return;
}int mid=querymx(1,0,n,l+1,r).second;
solve(l,mid-1);
upt(l,mid,r);
solve(mid,r);
}
int main(){
n=rd();
for(int i=1; i<=n; i++) c[i]=rd(),d[i]=rd();
build_d(1,1,n); build_c(1,0,n);
for(int i=1;i<=n;i++){
lft[i]=lft[i-1];
while(query_d(1,1,n,lft[i]+1,i)<i-lft[i])
++lft[i];
}
for(int i=1;i<=n;i++) f[i].mx=-INF;
f[0].val=1; build_f(1,0,n);
solve(0,n);
(f[n].mx>0)? printf("%d %d\n",f[n].mx,f[n].val): puts("NIE");
}