bzoj 4411 USACO16FEB 负载平衡Load Balancing(扫描线,树状数组,三分答案,前缀和)
2017-06-05 15:26
267 查看
题意:
给你一个矩阵,里面有些点,让你横向切一刀,纵向切一刀,使得得到的四个区域内的最大的点数最少。
思路:
首先考虑对于n大小在1000左右,直接二维前缀和预处理,枚举后,O(1)计算答案
对于n=100000,二维开不下,考虑加入数据结构维护区间和
于是加入树状数组,考虑直接枚举竖着的直线的横坐标,每次枚举时不断向树状数组加点(点的纵坐标)
这样对于外层循环的每一个i,当前的树状数组维护的都是横坐标
1~i 时,y方向上的点的区间个数和
下面举个例子
假设矩阵被分成这样 A3 A4
A1 A2
那么A1中点的个数即为树状数组中sum(y),
为了求出A3,A2,一开始时维护x和y方向的一维前缀和
那么 A3=sum[x]-A1 A2=sum[y]-A1
A4=n-A1-A2-A3
这样的话就能把四个部分各自的点的数量表示出来
那么现在问题来了
我们不可能直接for来枚举横着的直线的纵坐标
又发现
min{max(A1,A2,A3,A4)}的值是个类似a<0的抛物线图像
满足上凸性质
所以可以用三分法对横坐标处理
三分判断条件见程序
最后横着的直线的y坐标在l处
直接计算答案
稍有问题,不过能过官方数据的WA代码
#include<bits/stdc++.h>
using namespace std;
const int N=101000;
struct data{
int x,y;
}p
;
int n,temp[N*2],cnt=0;
int id[10*N];
int maxy=-1,maxx=-1,minx=1e9,miny=1e9;
int ans=1e9;
int e
,sumy[2*N],sumx[2*N];
bool comp(data a,data b)
{return a.x<b.x;}
int lowbit(int x){return x&(-x);}
void add(int x)
{
while(x<N*2)
{
e[x]++;
x+=lowbit(x);
}
}
int sum(int x)
{
int res=0;
while(x>0)
{
res+=e[x];
x-=lowbit(x);
}
return res;
}
void get_4(int *a,int x,int y)
{
a[1]=sum(y);
a[2]=sumy[y]-a[1];
a[3]=sumx[x]-a[1];
a[4]=n-a[1]-a[2]-a[3];
sort(a+1,a+5);
}
void pre()
{
cin>>n;
for(int i=1;i<=n;i++)
{
cin>>p[i].x>>p[i].y;
temp[++cnt]=p[i].x;
temp[++cnt]=p[i].y;
maxy=max(maxy,p[i].y);
maxx=max(maxx,p[i].x);
minx=min(minx,p[i].x);
miny=min(miny,p[i].y);
}
sort(temp+1,temp+cnt+1);
cnt=unique(temp+1,temp+cnt+1)-temp-1;
for(int i=1;i<=cnt;i++)id[temp[i]]=i;
sort(p+1,p+n+1,comp);
for(int i=1;i<=n;i++) sumy[id[p[i].y]]++;
for(int i=1;i<=cnt;i++) sumy[i]+=sumy[i-1];
for(int i=1;i<=n;i++) sumx[id[p[i].x]]++;
for(int i=1;i<=cnt;i++) sumx[i]+=sumx[i-1];
}
bool xiaoyu(int *a1,int *a2)
{
for(int i=4;i>=1;i--)
{
if(a1[i]>a2[i])return 0;
if(a1[i]<a2[i])return 1;
}
}
void solve()
{
int now=1;
for(int i=1;i<=n;i++)
{
for(int j=now;j<=n;j++)
if(p[j].x<=p[i].x)add(id[p[j].y]);
else{now=j;break;}
int l=id[miny],r=id[maxy];
int a1[5],a2[5],m1,m2;
while(l<r)
{
m1=l+(r-l)/3;
m2=r-(r-l)/3;
get_4(a1,id[p[i].x],m1);
get_4(a2,id[p[i].x],m2);
if(xiaoyu(a1,a2))
r=m2-1;
else l=m1+1;
}
get_4(a1,id[p[i].x],l);
ans=min(a1[4],ans);
}
cout<<ans;
}
int main()
{
// freopen("in.in","r",stdin);
pre();
solve();
}
给你一个矩阵,里面有些点,让你横向切一刀,纵向切一刀,使得得到的四个区域内的最大的点数最少。
思路:
首先考虑对于n大小在1000左右,直接二维前缀和预处理,枚举后,O(1)计算答案
对于n=100000,二维开不下,考虑加入数据结构维护区间和
于是加入树状数组,考虑直接枚举竖着的直线的横坐标,每次枚举时不断向树状数组加点(点的纵坐标)
这样对于外层循环的每一个i,当前的树状数组维护的都是横坐标
1~i 时,y方向上的点的区间个数和
下面举个例子
假设矩阵被分成这样 A3 A4
A1 A2
那么A1中点的个数即为树状数组中sum(y),
为了求出A3,A2,一开始时维护x和y方向的一维前缀和
那么 A3=sum[x]-A1 A2=sum[y]-A1
A4=n-A1-A2-A3
这样的话就能把四个部分各自的点的数量表示出来
那么现在问题来了
我们不可能直接for来枚举横着的直线的纵坐标
又发现
min{max(A1,A2,A3,A4)}的值是个类似a<0的抛物线图像
满足上凸性质
所以可以用三分法对横坐标处理
三分判断条件见程序
最后横着的直线的y坐标在l处
直接计算答案
稍有问题,不过能过官方数据的WA代码
#include<bits/stdc++.h>
using namespace std;
const int N=101000;
struct data{
int x,y;
}p
;
int n,temp[N*2],cnt=0;
int id[10*N];
int maxy=-1,maxx=-1,minx=1e9,miny=1e9;
int ans=1e9;
int e
,sumy[2*N],sumx[2*N];
bool comp(data a,data b)
{return a.x<b.x;}
int lowbit(int x){return x&(-x);}
void add(int x)
{
while(x<N*2)
{
e[x]++;
x+=lowbit(x);
}
}
int sum(int x)
{
int res=0;
while(x>0)
{
res+=e[x];
x-=lowbit(x);
}
return res;
}
void get_4(int *a,int x,int y)
{
a[1]=sum(y);
a[2]=sumy[y]-a[1];
a[3]=sumx[x]-a[1];
a[4]=n-a[1]-a[2]-a[3];
sort(a+1,a+5);
}
void pre()
{
cin>>n;
for(int i=1;i<=n;i++)
{
cin>>p[i].x>>p[i].y;
temp[++cnt]=p[i].x;
temp[++cnt]=p[i].y;
maxy=max(maxy,p[i].y);
maxx=max(maxx,p[i].x);
minx=min(minx,p[i].x);
miny=min(miny,p[i].y);
}
sort(temp+1,temp+cnt+1);
cnt=unique(temp+1,temp+cnt+1)-temp-1;
for(int i=1;i<=cnt;i++)id[temp[i]]=i;
sort(p+1,p+n+1,comp);
for(int i=1;i<=n;i++) sumy[id[p[i].y]]++;
for(int i=1;i<=cnt;i++) sumy[i]+=sumy[i-1];
for(int i=1;i<=n;i++) sumx[id[p[i].x]]++;
for(int i=1;i<=cnt;i++) sumx[i]+=sumx[i-1];
}
bool xiaoyu(int *a1,int *a2)
{
for(int i=4;i>=1;i--)
{
if(a1[i]>a2[i])return 0;
if(a1[i]<a2[i])return 1;
}
}
void solve()
{
int now=1;
for(int i=1;i<=n;i++)
{
for(int j=now;j<=n;j++)
if(p[j].x<=p[i].x)add(id[p[j].y]);
else{now=j;break;}
int l=id[miny],r=id[maxy];
int a1[5],a2[5],m1,m2;
while(l<r)
{
m1=l+(r-l)/3;
m2=r-(r-l)/3;
get_4(a1,id[p[i].x],m1);
get_4(a2,id[p[i].x],m2);
if(xiaoyu(a1,a2))
r=m2-1;
else l=m1+1;
}
get_4(a1,id[p[i].x],l);
ans=min(a1[4],ans);
}
cout<<ans;
}
int main()
{
// freopen("in.in","r",stdin);
pre();
solve();
}
相关文章推荐
- [扫描线 线段树上二分] BZOJ 4411 [Usaco2016 Feb]Load balancing
- [扫描线 树链剖分 树状数组] BZOJ2758 [SCOI2012]Blinker的噩梦
- 洛谷P3138 [USACO16FEB]负载平衡Load Balancing_Silver
- [扫描线 树状数组 随机数列 优化] BZOJ 2221 [Jsoi2009]面试的考验
- [SDOI2009][BZOJ1878][前缀和][树状数组]HH的项链
- [BZOJ4009][HNOI2015]接水果-整体二分-树状数组-扫描线
- [bzoj1717][Usaco2006 Dec]Milk Patterns 产奶的模式 (hash构造后缀数组,二分答案)
- 模拟——洛谷P3138 [USACO16FEB]负载平衡Load Balancing
- 树状数组【bzoj1782】: [Usaco2010 Feb]slowdown 慢慢游
- 【BZOJ1818】[Cqoi2010]内部白点 扫描线+树状数组
- 【BZOJ1044】【tyvj3511】【codevs1870】木棍分割,二分答案+滚动数组+前缀和DP
- 【bzoj2023/1630】[Usaco2005 Nov]Ant Counting 数蚂蚁 (DP+前缀和+滚动数组)
- BZOJ 1717: [Usaco2006 Dec]Milk Patterns 产奶的模式( 二分答案 + 后缀数组 )
- [bzoj2058][Usaco2010 Nov]Cow Photographs_树状数组_动态规划
- BZOJ 1878 HH的项链(树状数组)
- 【BZOJ】1692 & 1640: [Usaco2007 Dec]队列变换(后缀数组+贪心)
- 【splay】【树状数组】 BZOJ 1503 [NOI2004]郁闷的出纳员
- BZOJ 1878 SDOI 2009 HH的项链 树状数组 + 离线处理
- 【BZOJ】【P1106】【POI2007】【立方体大作战tet】【题解】【树状数组】
- 树状数组 BZOJ 1012 [JSOI2008]最大数maxnumber