[bzoj3711][PA2014]Druzyny【分治】【dp】

[题目描述]

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<R时,为了在O(L)的复杂度内得出答案。当右区间访问到mid+L时,左区间可行的右端点显然是mid,此时二分出左区间中还包含哪些g[i],在线段树上打标记更新答案。

具体见代码:

/**************************************************************
    Problem: 3711
    User: duweihua
    Language: C++
    Result: Accepted
    Time:26552 ms
    Memory:129412 kb
****************************************************************/
 
# include <bits/stdc++.h>
# 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].ans<ans) T[p].ans=ans, T[p].ansnum=0;
        if (T[p].ans==ans) {
            T[p].ansnum=(T[p].ansnum+ansnum);
            if (T[p].ansnum>P) 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(mid<l) modify_tag(T[p].pr,l,r,ans,ansnum);
            else {modify_tag(T[p].pl,l,mid,ans,ansnum); modify_tag(T[p].pr,mid+1,r,ans,ansnum);};
}
void query_tag(int p, int x){
    if (T[p].ans>f[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<l) getans(T[p].pr,l,r);
            else {getans(T[p].pl,l,mid); getans(T[p].pr,mid+1,r);}
}
int find_big(int pl, int pr, int x){
    int num=pr+1;
    while (pl<=pr){
        int mid=(pl+pr)>>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]<nowans+1) num[s]=0,f[s]=nowans+1;
        if (f[s]==nowans+1) num[s]=(num[s]+nownum)%P;
        }
    }
    else flag=false;
    for (s=s+1; s<=min(r,mid+c[mid]-2); s++){
        if (nowl>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 (nowans<f[nowr-1]) nowans=f[nowr-1], nownum=0;
                    if (nowans==f[nowr-1]) nownum=(nownum+num[nowr-1])%P; 
                }
            }
        }
        if (flag==false) continue;
        if (f[s]<nowans+1) num[s]=0, f[s]=nowans+1;
        if (f[s]==nowans+1) {
            num[s]=(num[s]+nownum); if (num[s]>P) 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; s<b2; s++){
        nowans=-inf, nownum=0; getans(rt,g[s]-1,mid-1);
        if (f[s]<nowans+1) num[s]=0, f[s]=nowans+1;
        if (f[s]==nowans+1) {
            num[s]=(num[s]+nownum); if (num[s]>P) 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)<i-g[i]+1) g[i]++;
    }   
    solve(1,n);
    if (f[n]<=0)
        printf("NIE\n");
        else printf("%d %d\n",f[n],num[n]);
    return 0;
}



posted @ 2018-01-17 15:54  Vanisher  阅读(234)  评论(0编辑  收藏  举报