我终于学会打开机房的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. 人机交互大作业---C#WinForm酒店预订系统(纯界面)

    登录: 所有界面: 源代码:最近较忙,后续会上传至github 材料参考:扬中菲尔斯金陵大酒店

  2. ubantu系统之 Ubuntu14.04安装Samba实现文件共享

    1 安装    sudo apt-get install samba  2 配置    打开Samba配置文件:    vim /etc/samba/smb.conf      在其最后添加:    ...

  3. Python找出列表中的最大数和最小数

    Python找出列表中数字的最大值和最小值 思路: 先使用冒泡排序将列表中的数字从小到大依次排序 取出数组首元素和尾元素 运行结果: 源代码: 1 ''' 2 4.编写函数,功能:找出多个数中的最大值 ...

  4. JDBC 4.0 开始Java操作数据库不用再使用 Class.forName加载驱动类了

    JDBC 4.0 开始Java操作数据库不用再使用 Class.forName加载驱动类了 代码示例 转自 https://docs.oracle.com/javase/tutorial/jdbc/o ...

  5. drf的基本使用、APIView源码分析和CBV源码拓展

    cbv源码拓展 扩展,如果我在Book视图类中重写dispatch方法 -可以实现,在get,post方法执行之前或者之后执行代码,完成类似装饰器的效果 def dispatch(self, requ ...

  6. html显示与隐藏元素的几种方式

    html显示与隐藏元素的几种方式 1.display none : 无 隐藏元素 block : 显示 转换为块级元素   不占位:当隐藏的时候元素就完全没有了.不能看见和操作该元素. 优点:为其他元 ...

  7. python的字典及相关操作

    一.什么是字典 字典是Python中最强大的数据类型之一,也是Python语言中唯一的映射类型.映射类型对象里哈希值(键,key)和指向的对象(值,value)是一对多的的关系,通常被认为是可变的哈希 ...

  8. javaWeb代码整理03-druid数据库连接池

    jar包: maven坐标: <dependency> <groupId>com.alibaba</groupId> <artifactId>druid ...

  9. python基础练习题(有一对兔子,从出生后第3个月起每个月都生一对兔子,小兔子长到第三个月后每个月又生一对兔子,假如兔子都不死,问每个月的兔子总数为多少?)

    day6 --------------------------------------------------------------- 实例011:养兔子 题目 有一对兔子,从出生后第3个月起每个月 ...

  10. 【职场必备】6个免费良心网站&职场办公网站(收藏血赚)

    1.随机自动生成头像的网站:https://www.tool22.com/Tools-SJTX.html2.迅捷PDF转换器:https://app.xunjiepdf.com/3.全网音乐下载:① ...