[日常摸鱼]poj3179-Corral the Cows——离散化+二维前缀和/KD-Tree
http://poj.org/problem?id=3179
题意:有一个\(10000\times 10000\)的网格,开始有\(n\)次操作,每次让一个位置\((x,y)\)的值+1(初始全为0),同时告诉你一个\(C\),你现在要找一个尽可能小的正方形使得这个正方形内格点的权值之和不小于\(C\)。\(n\leq 500\),空间限制\(64MB\)。
题解:最外面很显然还是一层二分答案,于是我们考虑怎么check一个答案\(x\)是否可行。
肯定要枚举正方形的一个角,再根据\(x\)确定另一个角算前缀和,但是这东西值域有点大,直接二维前缀和数组都开不下,而且如果枚举\(10000^2\)个起点时间也很危险。
然后就是想优化,注意到我们只要枚举出现过的所有\(x,y\)组成的\(O(n^2)\)个位置就行了:因为如果一个\(x/y\)从来没出现过,那这一整行/列的和是0,没有枚举的必要。嗯…那后面就是怎么求一个矩形的和。
我会KDT!
一位学傻的同学(我)上来就写了个KDT,然后疯狂TLE,开了O2优化才勉强卡过acwing上的数据,poj实在卡不动了(x)
#include<cstdio>
#include<algorithm>
#include<cmath>
#pragma GCC optimize(2)
using namespace std;
#define rep(i,a,b) for(int i=(a);i<=(b);i++)
const int N=505;
struct node
{
int x,y,sum;
}s[N];
int n,c,c1,c2,xl,xr,yl,yr,rt;
int xs[N],ys[N];
int ls[N],rs[N],U[N],D[N],L[N],R[N];
bool cmpx(node p,node q){return p.x<q.x;}
bool cmpy(node p,node q){return p.y<q.y;}
void update(int i)
{
L[i]=R[i]=s[i].x;
U[i]=D[i]=s[i].y;
s[i].sum=1+s[ls[i]].sum+s[rs[i]].sum;
if(ls[i])
L[i]=min(L[i],L[ls[i]]),R[i]=max(R[i],R[ls[i]]),
D[i]=min(D[i],D[ls[i]]),U[i]=max(U[i],U[ls[i]]);
if(rs[i])
L[i]=min(L[i],L[rs[i]]),R[i]=max(R[i],R[rs[i]]),
D[i]=min(D[i],D[rs[i]]),U[i]=max(U[i],U[rs[i]]);
}
int build(int l,int r,int dep)
{
if(l>r)return 0;
int mid=(l+r)>>1;
double agx=0,agy=0,vax=0,vay=0;
rep(i,l,r)agx+=s[i].x,agy+=s[i].y;
agx/=r-l+1;agy/=r-l+1;
rep(i,l,r)vax+=pow(agx-s[i].x,2),vay+=pow(agy-s[i].y,2);
if(vax>vay)nth_element(s+l,s+mid,s+r+1,cmpx);
else nth_element(s+l,s+mid,s+r+1,cmpy);
ls[mid]=build(l,mid-1,dep+1);
rs[mid]=build(mid+1,r,dep+1);
update(mid);
return mid;
}
int query(int x)
{
if(!x||L[x]>xr||R[x]<xl||U[x]<yl||D[x]>yr)return 0;
if(xl<=L[x]&&R[x]<=xr&&yl<=D[x]&&U[x]<=yr)return s[x].sum;
int res=0;
if(xl<=s[x].x&&s[x].x<=xr&&yl<=s[x].y&&s[x].y<=yr)
res+=1;
res+=(ls[x]?query(ls[x]):0)+(rs[x]?query(rs[x]):0);
return res;
}
int main()
{
scanf("%d%d",&c,&n);
rep(i,1,n)scanf("%d%d",&s[i].x,&s[i].y);
rt=build(1,n,1);
rep(i,1,n)xs[i]=s[i].x,ys[i]=s[i].y;
sort(xs+1,xs+n+1);
sort(ys+1,ys+n+1);
c1=unique(xs+1,xs+n+1)-(xs+1);
c2=unique(ys+1,ys+n+1)-(ys+1);
int l=1,r=1e4;
while(r>l)
{
int mid=(l+r)>>1;
bool flag=0;
rep(i,1,c1)
{
rep(j,1,c2)
{
xl=xs[i];yl=ys[j];
xr=xl+mid-1;yr=yl+mid-1;
if(query(rt)>=c)flag=1;
if(flag)break;
}
if(flag)break;
}
if(flag)r=mid;
else l=mid+1;
}
printf("%d",l);
return 0;
}
我会离散化!
把所有x,y一起离散化,用离散化之后的坐标跑二维前缀和,注意upper_bound和lower_bound的区别
#include<cstdio>
#include<algorithm>
#include<cmath>
#include<vector>
using namespace std;
#define rep(i,a,b) for(int i=(a);i<=(b);i++)
#define per(i,a,b) for(int i=(a);i>=(b);i--)
#define mp make_pair
#define pb push_back
#define fi first
#define se second
typedef long long ll;
typedef pair<int,int> pii;
typedef vector<int> vi;
const int N=1005;
const int M=1e4;
int n,c,s[N][N];
int x[N],y[N],b[N],cnt;
bool check(int x)
{
int t=lower_bound(b+1,b+cnt+1,b[cnt]-x+1)-b;
rep(i,1,t)rep(j,1,t)
{
int xr=upper_bound(b+1,b+cnt+1,b[i]+x-1)-b-1;
int yr=upper_bound(b+1,b+cnt+1,b[j]+x-1)-b-1;
if(s[xr][yr]-s[xr][j-1]-s[i-1][yr]+s[i-1][j-1]>=c)
return 1;
}
return 0;
}
int main()
{
scanf("%d%d",&c,&n);
rep(i,1,n)scanf("%d%d",&x[i],&y[i]);
rep(i,1,n){b[++cnt]=x[i];b[++cnt]=y[i];}
sort(b+1,b+cnt+1);
cnt=unique(b+1,b+cnt+1)-(b+1);
b[++cnt]=1e4+1;
rep(i,1,n)
{
int tx,ty;
tx=lower_bound(b+1,b+cnt+1,x[i])-b;
ty=lower_bound(b+1,b+cnt+1,y[i])-b;
s[tx][ty]++;
}
rep(i,1,cnt)rep(j,1,cnt)
s[i][j]+=s[i-1][j]+s[i][j-1]-s[i-1][j-1];
int l=1,r=1e4;
while(r>l)
{
int mid=(l+r)>>1;
if(check(mid))r=mid;
else l=mid+1;
}
printf("%d",l);
return 0;
}