我终于学会打开机房的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. java的内存泄露是如何发生的,如何避免和发现

    java的垃圾回收与内存泄露的关系:[新手可忽略不影响继续学习] 马克-to-win:上一节讲了,(i)对象被置成null.(ii)局部对象(无需置成null)当程序运行到右大括号.(iii)匿名对象 ...

  2. Spring Security的使用

    spring security使用目的:验证,授权,攻击防护. 原理:创建大量的filter和interceptor来进行请求的验证和拦截,以此来达到安全的效果. Spring Security主要包 ...

  3. 用 JS(JavaScript )实现增删改查

    JS小例题 学习内容: 需求 总结: 学习内容: 需求 用 JavaScript 实现简单增删改查 实现代码 <!DOCTYPE html PUBLIC "-//W3C//DTD HT ...

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

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

  5. Blazor组件自做二 : 使用JS隔离制作手写签名组件

    Blazor组件自做二 : 使用JS隔离制作手写签名组件 本文相关参考链接 JavaScript 模块中的 JavaScript 隔离 Viewer.js工程 Blazor组件自做一 : 使用JS隔离 ...

  6. vim 下几种比较省劲的方式(vi结合着用)

    Vim的几种模式 正常模式:可以使用快捷键命令,或按:输入命令行. 插入模式:可以输入文本,在正常模式下,按i.a.o等都可以进入插入模式. 可视模式:正常模式下按v可以进入可视模式, 在可视模式下, ...

  7. select下拉框获取下拉项值的问题

    新发现: select option如果里面不写value值,默认提交<option></option>中间的值. 切记:真正提交的值写在value属性里面,option之间只 ...

  8. vue 项目build后部署上去页面空白

    默认情况下vue-cli 会认为项目是部署在域名的根路径上. 但是当项目被部署到了一个子路径上,就要自己选定子路径. 比如项目被部署在了 https://www.ujapp.com/my-app, 则 ...

  9. go context详解

    Context通常被称为上下文,在go中,理解为goroutine的运行状态.现场,存在上下层goroutine context的传递,上层goroutine会把context传递给下层gorouti ...

  10. 【虚拟机】VMware-Ubuntu-安装与卸载

    VMware-Ubuntu 一.Ubuntu虚拟机安装 1.安装Ubuntu镜像 下载Ubuntu镜像文件,下载地址:http://www.ubuntu.com,点击download 下一步,到如下页 ...