zoukankan      html  css  js  c++  java
  • [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
    ");
            else printf("%d %d
    ",f[n],num[n]);
        return 0;
    }
    


  • 相关阅读:
    Python selenium —— 父子、兄弟、相邻节点定位方式详解
    Python爬虫入门教程石家庄链家租房数据抓取
    自动化测试 selenium 环境搭建
    python3爬虫(find_all用法等)
    python网络爬虫抓取动态网页并将数据存入数据库MySQL
    mvc设计模式和mvc框架的区别
    python正则表达式贪婪与非贪婪模式
    python3获取一个网页特定内容
    使用exceljs 前端导出表格并自定义样式和超链接
    如何在devtools中找到未使用 JavaScript 和 CSS 代码
  • 原文地址:https://www.cnblogs.com/Vanisher/p/9136051.html
Copyright © 2011-2022 走看看