2 条题解
-
0
提供一种 的做法。
实际上本题可以不需要二分。考虑枚举竖直栅栏 的 。用树状数组维护它左右的点。在树状数组上倍增找到最大的满足以下条件的 。
条件:画出直线 和 ,将平面分为左下、右下、左上、右上四个部分。设四个部分中的点数分别为 ,则 。说人话就是下面两块的点数的较大值不超过上面两块的点数较大值。
那么,假如竖直栅栏被钦定为直线 ,那么最优(使得 最小)的水平栅栏一定是直线 或者直线 。
证明:设水平栅栏下方两块的点数较大值为 ,上方两块的点数较大值为 。显然水平栅栏为直线 的时候 ,水平栅栏为直线 的时候 。设 ,则此时显然 不小于原来的 ,答案不减。 时同理。
时间复杂度 。可以离散化做到 。
由于我懒,所以代码实现没有离散化。
#include<bits/stdc++.h> using namespace std; typedef long long ll; typedef unsigned long long ull; int n,m,t1[1000005],t2[1000005],x,n1,n2; const int N=1e6; vector<int>g[1000005]; void add1(int x,int y){n1+=y;while(x<=N)t1[x]+=y,x+=x&-x;} void add2(int x,int y){n2+=y;while(x<=N)t2[x]+=y,x+=x&-x;} int calc() { int res=0,l1=0,l2=0,ret=0x3f3f3f3f; for(int i=1<<19;i;i>>=1)//树状数组上倍增,一个很好玩的trick //可以用线段树上二分代替 { int c1=l1+t1[res+i],c2=l2+t2[res+i]; if(max(c1,c2)<=max(n1-c1,n2-c2))res+=i,l1=c1,l2=c2; else ret=min(ret,max(c1,c2)); //不想写query函数查询y<=res+2的点个数,所以直接取了很多次多余的min } ret=min(ret,max(n1-l1,n2-l2)); return ret; } int main() { scanf("%d",&n); int mx=0; for(int i=1,x,y;i<=n;++i) scanf("%d%d",&x,&y),g[x].push_back(y), mx=max(mx,x+1),add2(y,1); int ans=0x3f3f3f3f; for(int i=2;i<=mx;i+=2)if(g[i-1].size()) { for(auto x:g[i-1])add1(x,1),add2(x,-1); ans=min(ans,calc()); } printf("%d\n",ans); } -
0
我看到这个题目是真的没思路,然后看题解还只有一个而且没几行讲思路,快哭了。
为了避免别人也有我这种糟糕的体验,这篇题解诞生了。
先看题目,最大值最小,学过OI的都知道这要用到二分,直接二分枚举答案再就好了。
再看要怎么写。
设我们枚举的答案是,则我们分出来的每个区域的奶牛都要小于
显而易见。我们枚举 ,用两个树状数组维护 上面区域和下面的奶牛数,然后求一个最优的 。
但是如果直接枚举,时间复杂度会直接到,很明显不能直接枚举。
再仔细想想,假设我们是从小到大枚举,那么上面区域的奶牛数会越来越少,下面的奶牛的数量会越来越多,所以对于上面区域,我们从小到大枚举,设左上区域奶牛数刚好时,对于下面区域,我们从大到小枚举,设左下区域奶牛数刚好时,则最优的就是
和都确定了,再计算出其他两个区域的奶牛数就可以了。
#include<iostream> #include<cstdio> #include<cstring> #include<algorithm> using namespace std; const int N = 1e5 + 10; inline int read() { int res=0; char ch=getchar(); while(ch<'0'||ch>'9')ch=getchar(); while(ch>='0'&&ch<='9')res=(res<<3)+(res<<1)+(ch^48),ch=getchar(); return res; } struct Cow{ int x,y; bool operator <(const Cow a) const{ return y<a.y; } }c[N]; int n,sb[N],xb[N]; #define lowbit(x) (x)&(-x) void change(int a[],int x,int k) { while(x<=n) a[x]+=k,x+=lowbit(x); } int query(int a[],int x) { int res=0; while(x>0) res+=a[x],x-=lowbit(x); return res; } bool check(int x) { memset(sb,0,sizeof(sb)); memset(xb,0,sizeof(xb)); for(int i=1;i<=n;i++) change(sb,c[i].x,1); int st=n,xt=0,zs=1,zx=n; for(int t,i=1,j=1;i<=n;i=j) { while(c[i].y==c[j].y) change(sb,c[j].x,-1),change(xb,c[j].x,1),st--,xt++,j++; while(zs<=n&&query(sb,zs)<=x) zs++; zs--; while(zx>0&&query(xb,zx)>x) zx--; t=min(zx,zs); if(xt-query(xb,t)<=x&&st-query(sb,t)<=x) return true; } return false; } pair<int,int>p[N]; int tot,mid,l,r,ans; int main() { n=read(); for(int i=1;i<=n;i++) c[i].x=read(),c[i].y=read(),p[i].first=c[i].x,p[i].second=i; sort(p+1,p+n+1); for(int i=1;i<=n;i++) { if(p[i].first!=p[i-1].first) tot++; c[p[i].second].x=tot; } sort(c+1,c+n+1); l=1,r=n; while(l<=r) { mid=(l+r)>>1; if(check(mid)) r=mid-1,ans=mid; else l=mid+1; } printf("%d\n",ans); return 0; }
- 1
信息
- ID
- 6694
- 时间
- 2000ms
- 内存
- 256MiB
- 难度
- 10
- 标签
- 递交数
- 2
- 已通过
- 1
- 上传者