「JOISC 2020 Day1」汉堡肉
阅读原文时间:2023年07月10日阅读:1

我终于学会打开机房的LOJ了!

LOJ3272

有\(n(n<=2*10^5)\)个矩形,让你找\(k(k<=4)\)个点可以覆盖所有矩形(点可重复),输出一种方案。(保证有解)

可以注意到k很小。

从边界考虑。找到x=max(l[]),y=min(r[]),x=max(d[]),y=min(u[])的四条关键线。四条关键线围成了一个关键矩形(注意:只考虑边线,是空心的)。

每条关键线必须要被覆盖,此时脑海中yy出了很多种情况。容易发现如果不选择矩形端点只会有一种情况(每条关键线都会覆盖上一个点),k只能为4。反之面对k<4时肯定会选择端点。

又发现如果会选择关键矩形端点,是一个很好的限制。

就可以暴力搜索k层,每次枚举选择的端点。删掉这个端点所覆盖的矩形,k--,继续递归……

复杂度是:\(O(4^k*n)\)

此时k<4如果有解就肯定找到解了。

当k=4时,还会有不选关键矩形端点,每条边上选一个点的情况。

目前有两个限制:

1.每个关键矩形只能被覆盖一次->每个矩形与关键矩形交出来的线段(区间)中至少有一个被覆盖。

2.每条关键线上有且只能选择一个(线段上的)点。

发现如果把矩形与关键矩形交出来的线段(区间)当做状态的话,就是一个选or不选的2-SAT问题。

步骤为:

  • 预处理出每个线段(保存来自的矩形即所在的关键线)

  • (限制1)对来自同一个矩形的线段分类讨论:

    1.如果该线段包含完了所在的整个关键线段,直接忽略(因为它肯定会被覆盖到,而且至少交了三条关键线段非常费事)。

    2.如果这个矩形交了两个线段idx,idy.则至少选一个,!idx->idy,!idy->idx

    3.如果只交了一个线段idx,就必须选择这条线段,!idx->idx。

    4.一个都没交(显然无解,不过题面保证有解则不可能出现情况4)

  • (限制2)枚举每条关键线段上的线段(这里直接可以映射为1维区间)

    每个区间向该关键线上其它与之不交的区间满足两者中最多一个(就idx->!idy,idy->!idx)。

    当然需要前后缀优化建图

    先按r排序建一排往前连的虚拟点(pre[x]->idx')(pre[x]->pre[x-1]),每个点二分前面离它最近的y.rpre[y](这样y以前的节点都能连到了_)

    再按l排序同理后缀优化建图……

  • 跑完Tarjan后找到了必须选择的线段。然后输出每条关键线段上必选线段的交上任意一点(我直接输出左端点了)

    ps.复杂度\(O(8*n)\)但常数巨大,不过冲1s还是可以的。

总结:写了很久,非常难写……

算我最近做过最恶心的图论题了……

点击查看代码

#include<bits/stdc++.h>
using namespace std;
const int N=2e5+5;
const int M=N<<3;
static char buf[1000000],*p1=buf,*p2=buf,obuf[1000000],*p3=obuf;
#define getchar() p1==p2&&(p2=(p1=buf)+fread(buf,1,1000000,stdin),p1==p2)?EOF:*p1++
#define putchar(x) (p3-obuf<1000000)?(*p3++=x):(fwrite(obuf,p3-obuf,1,stdout),p3=obuf,*p3++=x)
template<typename item>
inline void read(register item &x)
{
    x=0;register char c=getchar();
    while(c<'0'||c>'9')c=getchar();
    while(c>='0'&&c<='9')x=(x<<3)+(x<<1)+(c^48),c=getchar();
}
struct node {int l,r,d,u;}a[N],A[N];
struct cross {int l,r,opt,id;}cr[5],rc[5][N];
bool cmpR(cross u,cross v) {return u.r<v.r;}
bool cmpL(cross u,cross v) {return u.l<v.l;}
int step,inf=1e9,n,K,pth[5][2],acnt;
bool flag=0;
void dfs() {
    if(flag)return;
    if(!acnt) {
        for(int i=1;i<step;i++) printf("%d %d\n",pth[i][0],pth[i][1]);
        for(int i=step;i<=K;i++) printf("%d %d\n",pth[step-1][0],pth[step-1][1]);
        flag=1;return;
    }
    if(step==K+1) {return;}
    int x[2],y[2];
    x[0]=0,x[1]=inf,y[0]=0,y[1]=inf;
    for(int i=1;i<=acnt;i++) {
        x[0]=max(x[0],A[i].l);x[1]=min(x[1],A[i].r);
        y[0]=max(y[0],A[i].d);y[1]=min(y[1],A[i].u);
    }
    node ta[acnt+1];
    int tca=acnt;for(int i=1;i<=acnt;i++)ta[i]=A[i];
    for(int u=0;u<=1;u++) for(int v=0;v<=1;v++) {
        pth[step][0]=x[u],pth[step][1]=y[v];
        acnt=0;for(int i=1;i<=tca;i++) {
            if((pth[step][0]<ta[i].l)||(pth[step][0]>ta[i].r)||(pth[step][1]<ta[i].d)||(pth[step][1]>ta[i].u))A[++acnt]=ta[i];
        }
        step++;dfs();
        step--;acnt=tca;for(int i=1;i<=acnt;i++)A[i]=ta[i];
    }
}
void solve1() {
    for(int i=1;i<=n;i++) A[++acnt]=a[i];
    step=1;dfs();
}
int nxt[M<<1],to[M<<1],head[M],ecnt,nd,_[M],pre[M],suf[M],tot[5];
bool mark[M],In_s[M];
void add_edge(int u,int v) {nxt[++ecnt]=head[u];to[ecnt]=v;head[u]=ecnt;}
int dfn[M],low[M],st[M],SCC,Bl[M],Time,tp;
void Tarjan(int u) {
    In_s[st[++tp]=u]=1;dfn[u]=low[u]=++Time;
    for(int i=head[u];i;i=nxt[i]) {
        int v=to[i];
        if(!dfn[v]) Tarjan(v),low[u]=min(low[u],low[v]);
        else if(In_s[v])low[u]=min(low[u],dfn[v]);
    }
    if(low[u]==dfn[u]) {
        ++SCC;int v;
        do {
            v=st[tp--];In_s[v]=0;Bl[v]=SCC;
        }while(u!=v);
    }
}
int Cq,x[2],y[2];
void fc_Chose() {
    for(int i=2;i<=Cq;i+=2) {
        if(Bl[i]<Bl[_[i]]) {mark[i]=1;}
    }
    for(int t=0;t<4;t++) {
        int up=tot[t];
        int _emo=-1;
        for(int i=1;i<=up;i++) {
            int x=rc[t][i].id;
            if(!mark[x])continue;
            if(_emo==-1) _emo=rc[t][i].l;
            else _emo=max(_emo,rc[t][i].l);
        }
        if(t<2) printf("%d %d\n",x[t],_emo);
        else printf("%d %d\n",_emo,y[t-2]);
    }
}
void Build() {
    x[0]=0,x[1]=inf,y[0]=0,y[1]=inf;
    for(int i=1;i<=n;i++) {
        x[0]=max(x[0],a[i].l);x[1]=min(x[1],a[i].r);
        y[0]=max(y[0],a[i].d);y[1]=min(y[1],a[i].u);
    }
    if(x[0]>x[1])swap(x[0],x[1]);if(y[0]>y[1])swap(y[0],y[1]);
    nd=1;
    for(int i=1;i<=n;i++) {
        bool flag=1;int cnt=0;
        for(int t=0;t<=1;t++) {
            if(a[i].l<=x[t]&&x[t]<=a[i].r) {
                int L=max(a[i].d,y[0]),R=min(a[i].u,y[1]);
                if(L==y[0]&&R==y[1]) {flag=0;break;}
                cr[++cnt].l=L;cr[cnt].r=R;cr[cnt].opt=t;
            }
            if(a[i].d<=y[t]&&y[t]<=a[i].u) {
                int L=max(a[i].l,x[0]),R=min(a[i].r,x[1]);
                if(L==x[0]&&R==x[1]) {flag=0;break;}
                cr[++cnt].l=L;cr[cnt].r=R;cr[cnt].opt=t+2;
            }
        }
        if(!flag)continue;
        if(cnt==2) {
            int op1=cr[1].opt,op2=cr[2].opt;
            cr[1].id=++nd;_[nd]=nd+1;++nd;
            cr[2].id=++nd;_[nd]=nd+1;++nd;
            add_edge(nd-2,nd-1);add_edge(nd,nd-3);      //at least one
            rc[op1][++tot[op1]]=cr[1];rc[op2][++tot[op2]]=cr[2];
        }
        else {
            int op1=cr[1].opt;
            cr[1].id=++nd;_[nd]=nd+1;++nd;
            add_edge(nd,nd-1);
            rc[op1][++tot[op1]]=cr[1];
        }
    }
    Cq=nd;
    for(int t=0;t<4;t++) {
        int up=tot[t];
        if(!up)continue;
        sort(rc[t]+1,rc[t]+up+1,cmpR);
        for(int i=1;i<=up;i++) {pre[i]=++nd;if(i>1)add_edge(pre[i],pre[i-1]);add_edge(pre[i],_[rc[t][i].id]);}
        int rmn=rc[t][1].r;
        for(int i=1;i<=up;i++) {
            if(rmn<rc[t][i].l) {
                int l=1,r=i-1,y=-1;
                while(l<=r) {
                    int mid=(l+r)>>1;
                    if(rc[t][mid].r<rc[t][i].l) {y=mid;l=mid+1;}
                    else r=mid-1;
                }
                add_edge(rc[t][i].id,pre[y]);
            }
        }
        sort(rc[t]+1,rc[t]+up+1,cmpL);
        for(int i=up;i>=1;i--) {suf[i]=++nd;if(i<up)add_edge(suf[i],suf[i+1]);add_edge(suf[i],_[rc[t][i].id]);}
        int lmx=rc[t][up].l;
        for(int i=up;i>=1;i--) {
            if(lmx>rc[t][i].r) {
                int l=i+1,r=up,y=-1;
                while(l<=r) {
                    int mid=(l+r)>>1;
                    if(rc[t][mid].l>rc[t][i].r) {y=mid;r=mid-1;}
                    else l=mid+1;
                }
                add_edge(rc[t][i].id,suf[y]);
            }
        }
    }
}
void solve2() {
    Build();
//    printf("!%d\n",nd);
    for(int i=2;i<=nd;i++) if(!dfn[i])Tarjan(i);
    fc_Chose();
}
int main() {
    read(n);read(K);
    for(int i=1;i<=n;i++) read(a[i].l),read(a[i].d),read(a[i].r),read(a[i].u);
    solve1();
    if(!flag)solve2();
//    else printf("!");
    return 0;
}

手机扫一扫

移动阅读更方便

阿里云服务器
腾讯云服务器
七牛云服务器

你可能感兴趣的文章