【luogu】祭坛

题目链接:https://www.luogu.org/problemnew/show/P3415

思路:排序+线段树+扫描线;

类似的题在【SDOI2009】 虔诚的墓主人https://www.luogu.org/problemnew/show/P2154

和上题相似,我们先确定:每个水晶的贡献只可能在当前行或当前列

同样地,我们选择用线段树+扫描线消掉一维;我们分两步走;

  1. 扫描线从左往右扫,线段树在竖直方向,维护区间最大值。
  2. 充分利用数据,扫描线从右扫回来,线段树在竖直方向,维护区间和。

对于第一步,线段树维护扫过当前点之后当前行对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;
}

猜你喜欢

转载自blog.csdn.net/qq_39766648/article/details/79745706