关闭

BZOJ3711: [PA2014]Druzyny(分治)

34人阅读 评论(0) 收藏 举报
分类:

传送门

题意:

体育课上,n个小朋友排成一行(从1到n编号),老师想把他们分成若干组,每一组都包含编号连续的一段小朋友,每个小朋友属于且仅属于一个组。
第i个小朋友希望它所在的组的人数不多于d[i],不少于c[i],否则他就会不满意。
在所有小朋友都满意的前提下,求可以分成的组的数目的最大值,以及有多少种分组方案能达到最大值。

题解:
模拟赛上遇到的。。这道题思路不错。

Sii的合法决策集合。
mx[x]表示到x的合法最优方案的组数。
dp[x]表示到x的最优方案下的方案数。
可以想到朴素的DP转移:
mx[i]=maxjSi{mx[j]}+1
dp[x]=jS,mx[j]=max{mx[k]},kSdp[j]

发现带着S的限制想要优化这个DP是很难的,这时候通过分治转化限制条件是一种很好的思路。
考虑jSi集合需要满足的条件:

max{cj,cj+1,...,ci}ijmin{dj,dj+1,...,di}

发现如果只考虑d的限制,那么i的合法集合为[left[i],i],且left[i]单调不降。

当然如果加上c的限制,这个区间内的一些地方就被删除而成为了零散的几部分,然后分治的思路就是,我们枚举当前最大c的位置,那么右边区间所有到左边的决策都会以该分界点作为最大值,这时候限制就很明了了。

但是还有一个问题是,如果单纯的这样递归下去,复杂度

T(n)=T(x)+T(nx)+nT(n)=n2

并没有实质性的优化,这时候要考虑这个特殊的限制c要怎么用上,若当前分治中心为mid,可以明确的是决策区间随着右边枚举状态dp[i] 而往右挪,这时候更新状态并不需要O(n),考虑怎么优化这个DP:

1.找出第一个可行位置p(使得中心点满足条件)。
2.初始决策区间的lmax{left[p],l}rmin{mid1,pc[mid]}
3.假设左指针一直处于左区间开头,右指针每次暴力挪动1位,那么更新状态为O(1)。挪到左区间末尾停止,二分可以更新的状态的区域并在线段树上更新,时间复杂度O(logn)
4.暴力查找left[i]在区间中的位置,每个i只会查询一次,总体复杂度为O(nlogn)

比较有疑问的是第3个暴力为什么是nlogn而不是n2,这是因为每次暴力挪动右指针,次数不会超过左区间的长度,而枚举i不会超过右区间长度,所以这部分的复杂度为

T(n)=T(x)+T(nx)+min{x,nx}

类似启发式合并的复杂度分析,总时间为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");
}
0
0
查看评论
发表评论
* 以上用户言论只代表其个人观点,不代表CSDN网站的观点或立场

[bzoj3711]Druzyny

题目描述体育课上,n个小朋友排成一行(从1到n编号),老师想把他们分成若干组,每一组都包含编号连续的一段小朋友,每个小朋友属于且仅属于一个组。 第i个小朋友希望它所在的组的人数不多于d[i],不少于c[i],否则他就会不满意。 在所有小朋友都满意的前提下,求可以分成的组的数目的最大值,以及有多少...
  • WerKeyTom_FTD
  • WerKeyTom_FTD
  • 2017-05-26 16:55
  • 418

bzoj3711

题意: 体育课上,n个小朋友排成一行(从1到n编号),老师想把他们分成若干组,每一组都包含编号连续的一段小朋友,每个小朋友属于且仅属于一个组。 第i个小朋友希望它所在的组的人数不多于d[i],不少于c[i],否则他就会不满意。 在所有小朋友都满意的前提下,求可以分成的组的数目的最大值,以及有多...
  • u010600261
  • u010600261
  • 2017-02-07 22:45
  • 448

【PA2014】【BZOJ3711】Druzyny

Description体育课上,n个小朋友排成一行(从1到n编号),老师想把他们分成若干组,每一组都包含编号连续的一段小朋友,每个小朋友属于且仅属于一个组。 第i个小朋友希望它所在的组的人数不多于d[i],不少于c[i],否则他就会不满意。 在所有小朋友都满意的前提下,求可以分成的组的数目的最大...
  • CreationAugust
  • CreationAugust
  • 2016-02-15 20:01
  • 954

BZOJ3711: [PA2014]Druzyny

orz考虑dp,f[i]表示1~i至多分成几段,g[i]表示1~i分成f[i]段的方案数 转移的时候将c[i],d[i]的限制分开考虑 对于d[i]的限制,不难发现他有单调性,可以预处理L[i]表示L[i]~i-1的j满足d[i]的限制可以由j转移到i 然后考虑c[i]的限制,他没有单调性,于...
  • L_0_Forever_LF
  • L_0_Forever_LF
  • 2017-12-15 09:26
  • 72
    个人资料
    • 访问:40899次
    • 积分:3059
    • 等级:
    • 排名:第13248名
    • 原创:261篇
    • 转载:5篇
    • 译文:1篇
    • 评论:21条
    文章分类