题意:在一个n*n的二维空间上有任意个气球,你可以横着发射三发子弹,竖着发射三发子弹,且要求每连续两发子弹的间距要恰好等于r。每发子弹可以打掉一排或者一列的气球,问你最多可以打掉多少个气球。
题解:这题思路其实不难想。将每连续三行的气球合并放在线段树里,维护气球个数最大值。然后暴力枚举每三列气球,每次先将这三列的气球在线段树去掉,更新后再求得行最大值,就是答案,然后再把删除的点的更新回去。比赛的时候全队以为这样的时间复杂度是n^2logn,但其实不是。采用势能分析法,在线段树上每次删除一个点会影响到 X X-R X-2*R 3个值 最多操作1e5次 复杂度 6*n*logn(删了还要加回来。
#include
using namespace std;
const int N = 1e5+10;
vectorf1[N],f2[N];
int mx[4*N];
void up(int id,int l,int r,int pos,int val){
if(l==r){
mx[id]+=val;
return;
}
int mid = l+r>>1;
if(pos<=mid) up(id<<1,l,mid,pos,val);
else up(id<<1|1,mid+1,r,pos,val);
mx[id]=max(mx[id<<1],mx[id<<1|1]);
}
int main()
{
int n,r;
scanf("%d%d",&n,&r);
for(int i=1;i<=n;i++){
int x,y;
scanf("%d%d",&x,&y);
f1[x+1].push_back(y+1);
f2[y+1].push_back(x+1);
}
for(int i=1;i<=100001;++i){//每个左端点能贡献的答案
int val=f2[i].size();
if(i+r<=100001) val+=f2[i+r].size();
if(i+2*r<=100001) val+=f2[i+2*r].size();
up(1,1,100001,i,val);
}
int ans=0;
for(int i=1;i<=100001;++i){
if(f1[i].size()==0) continue;
int tmp=0,cnt=0;
for(int j=i;j>=1;j-=r){//将枚举三列的点删掉
if(cnt==3||j<1) break;
tmp+=f1[j].size();
cnt++;
for(int v:f1[j]){
up(1,1,100001,v,-1);
if(v-r>=1) up(1,1,100001,v-r,-1);
if(v-2*r>=1) up(1,1,100001,v-2*r,-1);
}
}
tmp+=mx[1];//加上线段树最大值
cnt=0;
for(int j=i;j>=1;j-=r){//将枚举三列的点添加
if(cnt==3||j<1) break;
cnt++;
for(int v:f1[j]){
up(1,1,100001,v,1);
if(v-r>=1) up(1,1,100001,v-r,1);
if(v-2*r>=1) up(1,1,100001,v-2*r,1);
}
}
ans=max(ans,tmp);
}
printf("%d\n",ans);
}