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; }
相关文章推荐
- JZOJ 5521 Try to find out the wrong in the test
- In a bunch of number to find out the biggest minimum value
- The transaction log for database 'NJ_Address' is full. To find out why space in the log cannot be
- The transaction log for database 'wcadmin' is full. To find out why space in the log cannot be reused, see the log_reuse_wait_desc column in sys.datab
- Unable to find the report in the manifest resources. Please build the project, and try again.
- How To Find The Top Wrinkle Cream In 5 Easy Steps
- How to test the code which maybe distroyed in the future ?
- Failed to find style '****Style' in current theme
- You need tcl 8.5 or newer in order to run the Redis test
- [ios] how to findout the language you use on ios devices.
- Find out all the elements in A and B such that the A[i]-B[j]=C[k]
- How-to find the SQL that using lots of temp tablespace in Oracle
- MyEclipse中无法部署项目到tomcat中的解决方法( deployment is out of date due to changes in the underlying ......)
- 签名问题一则:Unable to find manifest signing certificate in the certificate store
- [ERROR] Fatal error: Please read "Security" section of the manual to find out how to run mysqld as root!
- 布置Git环境Your working copy is out of date. Try pulling from the remote to get the latest change
- [eZ publish] how to test the variables that exist in the url
- The report is used to find enhancement and badi in sap.
- error MSB3323: Unable to find manifest signing certificate in the certificate st
- Unable to find manifest signing certificate in the certificate store