您的位置:首页 > 其它

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

2018-01-17 15:53 302 查看
[题目描述]

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

鸣谢Jcvb

[题解]

记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
,d
,g
,f
,num
,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_m
4000
ax_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
<=0)
printf("NIE\n");
else printf("%d %d\n",f
,num
);
return 0;
}

内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: