您的位置:首页 > 其它

Try to find out the wrong in the test

2018-01-15 17:31 302 查看

Description

有n个人排成一排,你需要对这n个人分组,每组必须是连续的一段。

每个人有要求,(c[i],d[i])表示这个人所在的组的最少人数和最多人数。

求最多能分成多少组和方案。

n<=1e6

Solution

如果只有d的限制这道题就很好做了。

因为d限制了我们i只能从i前面的一段区间转移过来,不妨设为left[i],显然left是单调的

但是有c的限制就很麻烦了,因为c的限制是一堆零碎的区间。

于是我们可以考虑按c的最大值分治。设c的最大值的位置为k,值为c。

我们先处理[l,k-1],然后考虑跨过k的转移。

这样子我们的下界就一定是k。

接下来我们考虑分类讨论

1:left[i] < l&&i<=k-1+c

这样子的点的转移区间是左边的一段区间,并且随着i的移动可转移的区间也不断扩大。

于是我们可以对第一个位置用线段树查出它的答案,接下来线性更新即可。

2:left[i ]< l&&i>k-1+c

符合条件的i也一定是一段区间,并且转移区间是整个左区间,于是直接用线段树维护就好了。

3:l<=left[i] < k

直接在线段树上查找。

4:left[i]>=k

分治到[k,r]区间。

但是我们这样不是常规的分治,如何考虑复杂度?

第1种情况最开始显然是O(n log n)的

考虑i的移动,最坏情况下i会移动min(k-l,r-k+1)次,也就是两边的长度取min

把分治过程看成启发式合并,这一部分也是O(n log n)的

第2种情况显然是O(n log n)的

第3种情况,对于每个点我们只会做一次,所以也是O(n log n)的

至此我们已经完美的解决了这道神题Orz

Code

#include <cstdio>
#include <cstring>
#include <algorithm>
#define fo(i,a,b) for(int i=a;i<=b;i++)
#define fd(i,a,b) for(int i=a;i>=b;i--)
using namespace std;

int read() {
char ch;
for(ch=getchar();ch<'0'||ch>'9';ch=getchar());
int x=ch-'0';
for(ch=getchar();ch>='0'&&ch<='9';ch=getchar()) x=x*10+ch-'0';
return x;
}

const int N=1e6+5,Mo=1e9+7;

#define max(a,b) ((a)>(b)?(a):(b))
#define min(a,b) ((a)<(b)?(a):(b))

struct node{
int mx,cnt;
node(int _mx=0,int _cnt=0) {mx=_mx;cnt=_cnt;}
friend node operator + (node y,node z) {
if (y.mx==-1) return z;
if (z.mx==-1) return y;
node x;x.mx=max(y.mx,z.mx);
if (y.mx==z.mx) x.cnt=(y.cnt+z.cnt)%Mo;
if (y.mx>z.mx) x.cnt=y.cnt;
if (y.mx<z.mx) x.cnt=z.cnt;
return x;
}
}tr[N<<2],lazy[N<<2],f
;

int n,mx[N<<2],c
,d
,q
,left
;

void update(int v,node z) {
tr[v]=tr[v]+z;lazy[v]=lazy[v]+z;
}

void down(int v) {
if (lazy[v].mx!=-1) {
update(v<<1,lazy[v]);
update(v<<1|1,lazy[v]);
lazy[v]=node(-1,0);
}
}

void build(int v,int l,int r) {
if (l==r) {mx[v]=l;return;}
int mid=l+r>>1;
build(v<<1,l,mid);
build(v<<1|1,mid+1,r);
if (c[mx[v<<1]]>=c[mx[v<<1|1]]) mx[v]=mx[v<<1];
else mx[v]=mx[v<<1|1];
}

int get_max(int v,int l,int r,int x,int y) {
if (l==x&&r==y) return mx[v];
int mid=l+r>>1;
if (y<=mid) return get_max(v<<1,l,mid,x,y);
else if (x>mid) return get_max(v<<1|1,mid+1,r,x,y);
else {
int a=get_max(v<<1,l,mid,x,mid);
int b=get_max(v<<1|1,mid+1,r,mid+1,y);
return (c[a]>=c[b])?a:b;
}
}

void modify(int v,int l,int r,int x,int y,node z) {
if (x>y) return;
if (l==x&&r==y) {update(v,z);return;}
int mid=l+r>>1;down(v);
if (y<=mid) modify(v<<1,l,mid,x,y,z);
else if (x>mid) modify(v<<1|1,mid+1,r,x,y,z);
else modify(v<<1,l,mid,x,mid,z),modify(v<<1|1,mid+1,r,mid+1,y,z);
tr[v]=tr[v<<1]+tr[v<<1|1];
}

node query(int v,int l,int r,int x,int y) {
if (x>y) return node(-1,0);
if (l==x&&r==y) return tr[v];
int mid=l+r>>1;down(v);
if (y<=mid) return query(v<<1,l,mid,x,y);
else if (x>mid) return query(v<<1|1,mid+1,r,x,y);
else return query(v<<1,l,mid,x,mid)+query(v<<1|1,mid+1,r,mid+1,y);
}

void init() {
n=read();
fo(i,1,n) c[i]=read(),d[i]=read();
int head=1,tail=0;
fo(i,1,n) {
while (head<=tail&&d[q[tail]]>d[i]) tail--;
q[++tail]=i;
for(int j=left[i-1];;j++) {
while (head<=tail&&q[head]<=j) head++;
if (i-j<=d[q[head]]) {left[i]=j;break;}
}
}
fo(i,0,n) f[i]=node(-1,0);f[0]=node(0,1);
fo(i,1,n<<2) tr[i]=lazy[i]=node(-1,0);
}

int find(int l,int r,int v) {
r++;
while (l<r) {
int mid=l+r>>1;
if (left[mid]<v) l=mid+1;
else r=mid;
}
return l-1;
}

void solve(int l,int r) {
if (l>r) return;
if (l==r) {
modify(1,0,n,l,l,f[l]);
f[l]=query(1,0,n,l,l);
return;
}
int k=get_max(1,0,n,l+1,r),mx=c[k];
solve(l,k-1);
int mid=max(l+mx,k);
if (left[mid]<l&&mid<=r) {
node now=query(1,0,n,l,mid-mx);
if (now.mx!=-1) {
now.mx++;
f[mid]=f[mid]+now;
now.mx--;
}
fo(i,mid+1,min(k-1+mx,r)) {
if (left[i]>=l) break;
now=now+f[i-mx];
if (now.mx!=-1) {
now.mx++;
f[i]=f[i]+now;
now.mx--;
}
}
}

mid=k+mx;
if (left[mid]<l&&mid<=r) {
node now=query(1,0,n,l,k-1);
if (now.mx!=-1) {
now.mx++;
modify(1,0,n,mid,find(mid,r,l),now);
}
}

mid=find(k,r,l)+1;
fo(i,mid,r) {
if (left[i]>=k) break;
node now=query(1,0,n,left[i],min(k-1,i-mx));
if (now.mx!=-1) {
now.mx++;
f[i]=f[i]+now;
}
}
solve(k,r);
}

int main() {
freopen("schooldays.in","r",stdin);
freopen("schooldays.out","w",stdout);
init();
build(1,0,n);
solve(0,n);
if (f
.mx==-1) puts("-1");
else printf("%d %d\n",f
.mx,f
.cnt);
return 0;
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: 
相关文章推荐