题目链接:https://www.luogu.org/problemnew/show/P3415
思路:排序+线段树+扫描线;
类似的题在【SDOI2009】 虔诚的墓主人https://www.luogu.org/problemnew/show/P2154
和上题相似,我们先确定:每个水晶的贡献只可能在当前行或当前列;
同样地,我们选择用线段树+扫描线消掉一维;我们分两步走;
- 扫描线从左往右扫,线段树在竖直方向,维护区间最大值。
- 充分利用数据,扫描线从右扫回来,线段树在竖直方向,维护区间和。
对于第一步,线段树维护扫过当前点之后当前行对ans的贡献,即扫过后当前行左右两侧水晶数最小值;
对于第二步,线段树维护扫过当前点之后当前行对sum的贡献,即扫过后当前行左右两侧水晶数最小值大于等于ans的,树上置为1,否则置为0。
这样问题就转化为了线段树单点修改区间求和的问题。
时间复杂度nlogn;
我们考虑用树状数组或zkw线段树来实现;
以下是zkw线段树代码↓(码风非常鬼畜清奇不要学我QwQ
#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>
using namespace std;
const int MAXN=(1<<17)-1;
int n,x,y,ans,sum;
int tree[MAXN<<1],cntl[MAXN],cntr[MAXN],cnto[MAXN],cntd[MAXN];
struct rpg{
int x,y;
}p[MAXN];
inline int read(){
int x=0;char ch=getchar();
while(ch<'0'||ch>'9') ch=getchar();
while(ch>='0'&&ch<='9') x=(x<<3)+(x<<1)+(ch^'0'),ch=getchar();
return x;
}
bool cmp(rpg a,rpg b){
if(a.x==b.x) return a.y<b.y;
return a.x<b.x;
}
void init(){
scanf("%d",&n);
for(int i=1;i<=n;++i){
x=read(),y=read();
p[i]=(rpg){x,y};
++cntr[y];++cnto[x];
}sort(p+1,p+n+1,cmp);
return;
}
void solve1(){
for(int i=1;i<=n;++i){
--cntr[p[i].y];++cntl[p[i].y];
--cnto[p[i].x];++cntd[p[i].x];
tree[MAXN+p[i].y]=min(cntl[p[i].y],cntr[p[i].y]);
for(int j=MAXN+p[i].y>>1;j;j>>=1) tree[j]=max(tree[j<<1],tree[j<<1|1]);
if(p[i].x==p[i-1].x&&p[i].y-p[i-1].y>1){
int mxw=0;
int l=MAXN+p[i-1].y,r=MAXN+p[i].y;
while(l^r^1){
if(~l&1) mxw=max(mxw,tree[l^1]);
if(r&1) mxw=max(mxw,tree[r^1]);
l>>=1;r>>=1;
}mxw=min(mxw,min(cnto[p[i].x]+1,cntd[p[i].x]-1));
ans=max(ans,mxw);
}
}printf("%d\n",ans);
return;
}
void solve2(){
for(int i=n;i;--i){
--cntl[p[i].y];++cntr[p[i].y];
--cntd[p[i].x];++cnto[p[i].x];
int mxw=min(cntl[p[i].y],cntr[p[i].y]);
if(mxw>=ans){
tree[MAXN+p[i].y]=1;
for(int j=MAXN+p[i].y>>1;j;j>>=1) tree[j]=tree[j<<1]+tree[j<<1|1];
}else{
tree[MAXN+p[i].y]=0;
for(int j=MAXN+p[i].y>>1;j;j>>=1) tree[j]=tree[j<<1]+tree[j<<1|1];
}if(p[i+1].x==p[i].x&&p[i+1].y-p[i].y>1&&min(cntd[p[i].x]+1,cnto[p[i].x]-1)>=ans){
int l=MAXN+p[i].y,r=MAXN+p[i+1].y;
while(l^r^1){
if(~l&1) sum+=tree[l^1];
if(r&1) sum+=tree[r^1];
l>>=1;r>>=1;
}
}
}printf("%d\n",sum);
return;
}
int main(){
init();
solve1();
solve2();
return 0;
}