【JZOJ3463】军训
2016-06-30 18:39
323 查看
Description
HYSBZ 开学了!今年HYSBZ 有n 个男生来上学,学号为1…n,每个学生都必须参加军训。在这种比较堕落的学校里,每个男生都会有Gi 个女朋友,而且每个人都会有一个欠扁值Hi。学校为了保证军训时教官不会因为学生们都是人生赢家或者是太欠扁而发生打架事故,所以要把学生们分班,并做出了如下要求:1.分班必须按照学号顺序来,即不能在一个班上出现学号不连续的情况。
2.每个学生必须要被分到某个班上。
3.每个班的欠扁值定义为该班中欠扁值最高的那名同学的欠扁值。所有班的欠扁值之和不得超过Limit。
4.每个班的女友指数定义为该班中所有同学的女友数量之和。在满足条件1、2、3 的情况下,分班应使得女友指数最高的那个班的女友指数最小。
输入数据保证题目有解。
Solution
首先这题最大值最小,立即二分答案。一开始我还以为很简单的贪心就能过(然而我还是Too young)……
首先这道题40%的数据,可以想到O(n2log2n)的方法(其中Fi表示到i的最小欠扁值和):
Fi=min(Fj(∑k=i+1jgi≤mid)+max(hk)(j<k≤i))
接下来我们观察上面的式子,首先,j只用一个指针模拟即可(显然),然后是max(hk)(j<k≤i)),我们发现它们太恶心,还有min(Fj)(1≤j<i)等。
到这里,大神纷纷用stl,而蒟蒻的我只能在这打线段树。
首先max(hk)是从左到右单调递减的,可以用线段树维护(区间修改,查询),然后是min(Fj)可以逐次加入,逐次查询Fj+max(hk)的最小值。
于是,我们收获了一棵复杂度较大的线段树。
这样单次二分复杂度降到了O(nlog2n),总复杂度就是O(nlog22n)。
Code
小心点,不要迷失!时限2s,我用了1.107s……
#include<iostream> #include<cstdio> #include<cstdlib> #include<cstring> #define fo(i,j,k) for(int i=j;i<=k;i++) #define fd(i,j,k) for(int i=j;i>=k;i--) #define N 20001 #define M 1001 #define ll long long #define inf 100000000 using namespace std; int f ,g ,h ; struct node { int mh,mf,mz,lazy; }tr[N*4]; int n,lim; int p=0; ll cnt=0; void put(int v) { if(!tr[v].lazy) return; tr[v*2].mh=max(tr[v*2].mh,tr[v].lazy); tr[v*2+1].mh=max(tr[v*2+1].mh,tr[v].lazy); tr[v*2].mz=tr[v*2].mf+tr[v*2].mh; tr[v*2+1].mz=tr[v*2+1].mf+tr[v*2+1].mh; tr[v*2].lazy=max(tr[v*2].lazy,tr[v].lazy); tr[v*2+1].lazy=max(tr[v*2+1].lazy,tr[v].lazy); tr[v].lazy=0; } void update(int v) { tr[v].mh=max(tr[v*2].mh,tr[v*2+1].mh); tr[v].mf=min(tr[v*2].mf,tr[v*2+1].mf); tr[v].mz=min(tr[v*2].mz,tr[v*2+1].mz); } int pos=0; int findpos(int v,int l,int r,int z) { if(l==r) return l; int mid=(l+r)/2; put(v); if(tr[v*2+1].mh>z) return findpos(v*2+1,mid+1,r,z); else if(tr[v*2].mh>z) return findpos(v*2,l,mid,z); update(v); return 0; } void find(int v,int l,int r,int x,int y,int z) { if(l==x && r==y) { if(!pos && tr[v].mh>=z) pos=findpos(v,l,r,z); return; } if(pos) return; int mid=(l+r)/2; put(v); if(x>mid) find(v*2+1,mid+1,r,x,y,z); else if(y<=mid) find(v*2,l,mid,x,y,z); else { find(v*2+1,mid+1,r,mid+1,y,z); find(v*2,l,mid,x,mid,z); } update(v); } void change(int v,int l,int r,int x,int y,int z) { if(l==x && r==y) { tr[v].mh=z; tr[v].mz=z+tr[v].mf; tr[v].lazy=z; return; } int mid=(l+r)/2; put(v); if(y<=mid) change(v*2,l,mid,x,y,z); else if(x>mid) change(v*2+1,mid+1,r,x,y,z); else { change(v*2,l,mid,x,mid,z); change(v*2+1,mid+1,r,mid+1,y,z); } update(v); } void insert(int v,int l,int r,int x,int z) { if(l==r && l==x) { tr[v].mf=z; tr[v].mz=z+tr[v].mh; return; } int mid=(l+r)/2; put(v); if(x<=mid) insert(v*2,l,mid,x,z); else insert(v*2+1,mid+1,r,x,z); update(v); } void findans(int v,int l,int r,int x,int y) { if(l==x && r==y) { p=min(p,tr[v].mz); return; } int mid=(l+r)/2; put(v); if(y<=mid) findans(v*2,l,mid,x,y); else if(x>mid) findans(v*2+1,mid+1,r,x,y); else { findans(v*2,l,mid,x,mid); findans(v*2+1,mid+1,r,mid+1,y); } } bool check(int x) { memset(tr,0,sizeof(tr)); int j=1; fo(i,1,n) { if(g[i]-g[i-1]>x) return false; while(g[i]-g[j-1]>x) j++; pos=0; find(1,1,n,j,i,h[i]); change(1,1,n,max(pos+1,j),i,h[i]); p=inf; findans(1,1,n,j,i); f[i]=p; if(i<n) insert(1,1,n,i+1,f[i]); } if(f <=lim) return true; return false; } int main() { cin>>n>>lim; fo(i,1,n) scanf("%d %d",&h[i],&g[i]),g[i]+=g[i-1]; int l=1,r=g ; while(l<r) { int mid=(l+r)/2; if(check(mid)) r=mid; else l=mid+1; } cout<<l; }
相关文章推荐
- Thinking in java开篇
- 全志H3平台CLOCK简析
- JavaSE 基础 第45节Java异常快速入门
- C语言职工信息管理系统
- Problem-K
- C++随记
- linux下,shell如何删除指定字符间的内容
- iOS下将照片保存到相册的三种方法
- 捕获EF提交异常
- 关于并发的问题:乐观锁和悲观锁
- C++模板的特化与偏特化
- fuel8:vmware workstation上的安装
- Penetration Testing Tools Cheat Sheet 20160630
- Spring:com.sun.proxy.$proxy0 cannot be cast to XXX
- 私有的静态成员变量
- 通货膨胀与通货紧缩
- JavaSE 基础 第44节 引用外部类的对象
- infa规范器组件实现行列转换
- centos接口压力测试apache bench(ab)压力测试
- spring整合redis