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

description

LOJ3272

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

Solution

可以注意到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.r<x.l的y,idx->pre[y](这样y以前的节点都能连到了_

    再按l排序同理后缀优化建图……
  • 跑完Tarjan后找到了必须选择的线段。然后输出每条关键线段上必选线段的交上任意一点(我直接输出左端点了)

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

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

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

code

点击查看代码
#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;
}

「JOISC 2020 Day1」汉堡肉的更多相关文章

  1. 【LOJ】#3032. 「JOISC 2019 Day1」馕

    LOJ#3032. 「JOISC 2019 Day1」馕 处理出每个人把馕切成N段,每一段快乐度相同,我们选择第一个排在最前的人分给他的第一段,然后再在未选取的的人中选一个第二个排在最前的切一下,并把 ...

  2. 【LOJ】#3031. 「JOISC 2019 Day1」聚会

    LOJ#3031. 「JOISC 2019 Day1」聚会 听说随机可过? 我想了很久想了一个不会被卡的做法,建出前\(u - 1\)个点的虚树,然后找第\(u\)个点的插入位置,就是每次找一条最长链 ...

  3. 【LOJ】#3030. 「JOISC 2019 Day1」考试

    LOJ#3030. 「JOISC 2019 Day1」考试 看起来求一个奇怪图形(两条和坐标轴平行的线被切掉了一个角)内包括的点个数 too naive! 首先熟练的转化求不被这个图形包含的个数 -- ...

  4. 「JOISC 2014 Day1」巴士走读

    「JOISC 2014 Day1」巴士走读 将询问离线下来. 从终点出发到起点. 由于在每个点(除了终点)的时间被过来的边固定,因此如果一个点不被新的边更新,是不会发生变化的. 因此可以按照时间顺序, ...

  5. 「JOISC 2014 Day1」 历史研究

    「JOISC 2014 Day1」 历史研究 Solution 子任务2 暴力,用\(cnt\)记录每种权值出现次数. 子任务3 这不是一个尺取吗... 然后用multiset维护当前的区间,动态加, ...

  6. 「JOISC 2014 Day1」历史研究 --- 回滚莫队

    题目又臭又长,但其实题意很简单. 给出一个长度为\(N\)的序列与\(Q\)个询问,每个询问都对应原序列中的一个区间.对于每个查询的区间,设数\(X_{i}\)在此区间出现的次数为\(Sum_{X_{ ...

  7. 「题解」「JOISC 2014 Day1」历史研究

    目录 题目 考场思考 思路分析及标程 题目 点这里 考场思考 大概是标准的莫队吧,离散之后来一个线段树加莫队就可以了. 时间复杂度 \(\mathcal O(n\sqrt n\log n)\) . 然 ...

  8. 「JOISC 2020 Day4」首都城市

    题目   点这里看题目. 分析   做法比较容易看出来.我们对于每个城市,找出那些 " 如果这个城市在首都内,则必须在首都内的其它城市 " ,也就是为了让这个城市的小镇连通而必须选 ...

  9. 「JOISC 2020 Day2」变态龙之色 题解

    题目传送门 注意 同性必定不同色 必有一个同色异性,且不相互不喜欢 Solution 我们发现,我们问题比较大的就是如何确定性别问题.我们可以一个一个加进去,在原来已经确定了的二分图上增加新的性别关系 ...

随机推荐

  1. 网络安全—xss

    1.xss的攻击原理 需要了解 Http cookie ajax,Xss(cross-site scripting)攻击指的是攻击者往Web页面里插入恶意html标签或者javascript代码.比如 ...

  2. Linux 0.11源码阅读笔记-块设备驱动程序

    块设备驱动程序 块设备驱动程序负责实现对块设备数据的读写功能.内核代码统一使用缓冲块间接和块设备(如磁盘)交换数据,缓冲区数据通过块设备驱动程序和块设备交换数据. 块设备的管理 块设备表 内核通过一张 ...

  3. javaweb图书管理系统之账号密码验证登录

    验证账号与密码是否正确功能 一.注册功能 首先,在验证账号与密码是否正确的前提下的,需要先注册一个账号,如果没有账号,就会进不去,也无法验证. 其实,注册功能就是一个添加的功能,仿照我的第一篇文章,往 ...

  4. 【Android开发】安卓炫酷效果集合

    1. android-ripple-background 能产生波浪效果的背景图片控件,可以自定义颜色,波浪扩展的速度,波浪的圈数. github地址 2. android-shapeLoadingV ...

  5. java中方法重载是什么, 实际中到底有什么意义, 用处?请举例

    7.3 方法重载(overload)  当几个方法有相同的方法名,但参数个数不同或参数类型不同时,就涉及方法重载 方法重载有什么意义呢?在公司里编程,有时候一个方法名,要用到很多次,而且每次跟每次的参 ...

  6. Python简单文件读写

    ''' 用文件存储账户信息 使用列表存储多个账户信息,每个账户为一个字典对象 ''' users=[] #创建一个空列表 users.append({'id':'admin','pwd':'1235@ ...

  7. vue后台管理系统组件弹窗

    //addFormVisibleIcon可在data中设置true与falsehttps://element.eleme.io/#/zh-CN/component/installation <e ...

  8. 体验了一把最近很火的开源项目-MASA Blazor

    前言 很惭愧直到去年底才接触到Blazor.那什么是Blazor呢?相信大家都看过官方文档的详细说明,另外MASA团队也有不错的说明介绍 .用官方的话说Blazor是一个交互式客户端Web UI的框架 ...

  9. Golang | 并发

    goroutine 协程(Coroutine) Golang 在语言层面对并发编程进行了支持,使用了一种协程(goroutine)机制, 协程本质上是一种用户态线程,不需要操作系统来进行抢占式调度,但 ...

  10. 通过nfs将centos目录挂载到windows 系统的磁盘上

    环境:centos8,windows7 1.在centos上安装nfs服务 yum -y install nft-utils 2.启动nfs服务 systemctl start nfs-server ...