[题目描述]
3711: [PA2014]Druzyny
Time Limit: 40 Sec
Memory Limit: 128 MB
Submit: 343
Solved: 74
[ Submit][ Status][ Discuss]
Description
体育课上,n个小朋友排成一行(从1到n编号),老师想把他们分成若干组,每一组都包含编号连续的一段小朋友,每个小朋友属于且仅属于一个组。
第i个小朋友希望它所在的组的人数不多于d[i],不少于c[i],否则他就会不满意。
在所有小朋友都满意的前提下,求可以分成的组的数目的最大值,以及有多少种分组方案能达到最大值。
Input
第一行一个整数n(1<=n<=1000000),表示小朋友的数目。
接下来n行,每行两个整数c[i],d[i](1<=c[i]<=d[i]<=n),表示i所在组的人数的最小值和最大值。
Output
如果不存在这样的方案,仅输出一行NIE。
否则输出一行包含两个整数,组的数目的最大值、方案数量。(方案数量对1000000007取模)
Sample Input
样例输入1:
9
1 4
2 5
3 4
1 5
1 1
2 5
3 5
1 3
1 1
样例输入2:
2
1 1
2 2
Sample Output
样例输出1:
5 2
样例输出2:
NIE
HINT
Source
[题解]
记f[i]表示选择已经选取1..i,且i为右端点的答案,暴力转移是O(n^2)
不难发现,如果只考虑d[i],那么i作为右端点,可行的左端点的选择区间是单调增的,记为g[i],可以用线段树在O(n log n)求出。
考虑分治,每次选取当前区间[l..r]内c[i]最大的作为分治中点mid。
考虑[l..mid]对[mid..r] 的贡献
那么若只有c[i]的限制,[mid..r]中每一个i可行的转移区间的的左端点都是l,右端点每次只会+1,i变化时可以O(1)更新答案
考虑d[i]的限制,每次g[i]>当前左端点时,用线段树更新答案。由于每个g[i]只会对之前的一个分治区间产生影响。所以左端点的右移的复杂度的和是O(n log n)的。
为了保证分治的复杂度,左边对右边的影响要在min(左区间大小,右区间大小)处理出来,当L>R时复杂度显然是O(R)的,当L
具体见代码:
/**************************************************************
Problem: 3711
User: duweihua
Language: C++
Result: Accepted
Time:26552 ms
Memory:129412 kb
****************************************************************/
# include
# define N 1000010
# define inf 1e9
# define P 1000000007
using namespace std;
struct node{
int pl,pr,mn,mx,mxid,ans,ansnum,l,r,tans,tnum;
}T[N*2+100];
int c[N],d[N],g[N],f[N],num[N],place,rt,nowans,nownum,n,ti,cnt;
int read(){
int tmp=0,fh=1; char ch=getchar();
while (ch<'0'||ch>'9'){if (ch=='-') fh=-1; ch=getchar();}
while (ch>='0'&&ch<='9'){tmp=tmp*10+ch-'0'; ch=getchar();}
return tmp*fh;
}
void change(int x){
T[x].tnum=0;
T[x].tans=max(T[T[x].pl].tans,T[T[x].pr].tans);
if (T[x].tans==T[T[x].pl].tans) T[x].tnum=T[x].tnum+T[T[x].pl].tnum;
if (T[x].tans==T[T[x].pr].tans) T[x].tnum=(T[x].tnum+T[T[x].pr].tnum)%P;
}
int build(int l, int r){
int p=++place; T[p].l=l; T[p].r=r;
T[p].ans=-inf; T[p].ansnum=0; T[p].tans=-inf;
if (l==r){ T[p].mn=d[l]; T[p].mx=c[l]; T[p].mxid=l;}
else {
int mid=(T[p].l+T[p].r)>>1;
T[p].pl=build(l,mid); T[p].pr=build(mid+1,r);
T[p].mn=min(T[T[p].pl].mn,T[T[p].pr].mn); T[p].mx=max(T[T[p].pl].mx,T[T[p].pr].mx);
if (T[p].mx==T[T[p].pl].mx) T[p].mxid=T[T[p].pl].mxid; else T[p].mxid=T[T[p].pr].mxid;
}
return p;
}
int querymin(int p, int l, int r){
if (T[p].l==l&&T[p].r==r) return T[p].mn;
int mid=(T[p].l+T[p].r)>>1;
if (r<=mid) return querymin(T[p].pl,l,r);
else if (l>mid) return querymin(T[p].pr,l,r);
else return min(querymin(T[p].pl,l,mid),querymin(T[p].pr,mid+1,r));
}
int query_max_id(int p, int l, int r){
if (T[p].l==l&&T[p].r==r) return T[p].mxid;
int mid=(T[p].l+T[p].r)>>1;
if (r<=mid) return query_max_id(T[p].pl,l,r);
else if (l>mid) return query_max_id(T[p].pr,l,r);
else {
int now1=query_max_id(T[p].pl,l,mid), now2=query_max_id(T[p].pr,mid+1,r);
if (c[now1]>=c[now2]) return now1; else return now2;
}
}
void modify(int p, int x, int ans, int ansnum){
if (T[p].l==T[p].r){T[p].tans=ans; T[p].tnum=ansnum; return;}
int mid=(T[p].l+T[p].r)>>1;
if (mid>=x) modify(T[p].pl,x,ans,ansnum); else modify(T[p].pr,x,ans,ansnum);
change(p);
}
void modify_tag(int p, int l, int r, int ans, int ansnum){
if (T[p].l==l&&T[p].r==r){
if (T[p].ansP) T[p].ansnum-=P;
}
return;
}
int mid=(T[p].l+T[p].r)/2;
if (mid>=r) modify_tag(T[p].pl,l,r,ans,ansnum);
else if(midf[x]) f[x]=T[p].ans, num[x]=0;
if (T[p].ans==f[x]) {
num[x]=(num[x]+T[p].ansnum);
if (num[x]>P) num[x]-=P;
}
if (T[p].l==T[p].r) return;
int mid=(T[p].l+T[p].r)>>1;
if (mid>=x) query_tag(T[p].pl,x); else query_tag(T[p].pr,x);
}
void getans(int p, int l, int r){
if (T[p].l==l&&T[p].r==r){
if (T[p].tans>nowans) nowans=T[p].tans, nownum=0;
if (T[p].tans==nowans) nownum=(nownum+T[p].tnum)%P;
return;
}
int mid=(T[p].l+T[p].r)>>1;
if (mid>=r) getans(T[p].pl,l,r);
else if (mid>1;
if (g[mid]>=x){
pr=mid-1;
num=mid;
}
else pl=mid+1;
}
return num;
}
void solve(int l, int r){
if (l>r) return;
int mid=query_max_id(rt,l,r);
solve(l,mid-1);
int s=max(c[mid]+l-1,mid),nowl=max(l,g[s]), nowr=min(s-c[mid]+1,mid),lar;
nowans=-inf;
bool flag=true;
if (nowl<=nowr&&s<=r){
getans(rt,nowl-1,nowr-1);
if (s<=r){
if (f[s]mid) break;
lar=nowr; nowr=min(nowr+1,mid);
if (g[s]>nowl){
nowl=g[s];
nowans=-inf;
if (nowl<=nowr) getans(rt,nowl-1,nowr-1);
else flag=false;
}
else {
if (flag==false){
if (nowl<=nowr){
flag=true;
nowans=f[nowl-1];
nownum=num[nowl-1];
}
}
else {
if (nowr>lar){
if (nowansP) num[s]-=P;
}
}
int b1=find_big(s,r,l), b2=find_big(s,r,mid+1);
nowans=-inf, nownum=0; getans(rt,l-1,mid-1);
if (s<=b1-1) modify_tag(rt,s,b1-1,nowans+1,nownum);
for (int s=b1; sP) num[s]-=P;
}
}
query_tag(rt,mid);
modify(rt,mid,f[mid],num[mid]);
solve(mid+1,r);
}
int main(){
n=read();
for (int i=1; i<=n; i++)
c[i]=read(), d[i]=read();
for (int i=1; i<=n; i++) f[i]=-inf;
rt=build(0,n); g[1]=1; modify(rt,0,0,1); f[0]=0; num[0]=1;
for (int i=2; i<=n; i++){
g[i]=g[i-1];
while (querymin(rt,g[i],i)