「JOISC 2020 Day1」汉堡肉
我终于学会打开机房的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」汉堡肉的更多相关文章
- 【LOJ】#3032. 「JOISC 2019 Day1」馕
LOJ#3032. 「JOISC 2019 Day1」馕 处理出每个人把馕切成N段,每一段快乐度相同,我们选择第一个排在最前的人分给他的第一段,然后再在未选取的的人中选一个第二个排在最前的切一下,并把 ...
- 【LOJ】#3031. 「JOISC 2019 Day1」聚会
LOJ#3031. 「JOISC 2019 Day1」聚会 听说随机可过? 我想了很久想了一个不会被卡的做法,建出前\(u - 1\)个点的虚树,然后找第\(u\)个点的插入位置,就是每次找一条最长链 ...
- 【LOJ】#3030. 「JOISC 2019 Day1」考试
LOJ#3030. 「JOISC 2019 Day1」考试 看起来求一个奇怪图形(两条和坐标轴平行的线被切掉了一个角)内包括的点个数 too naive! 首先熟练的转化求不被这个图形包含的个数 -- ...
- 「JOISC 2014 Day1」巴士走读
「JOISC 2014 Day1」巴士走读 将询问离线下来. 从终点出发到起点. 由于在每个点(除了终点)的时间被过来的边固定,因此如果一个点不被新的边更新,是不会发生变化的. 因此可以按照时间顺序, ...
- 「JOISC 2014 Day1」 历史研究
「JOISC 2014 Day1」 历史研究 Solution 子任务2 暴力,用\(cnt\)记录每种权值出现次数. 子任务3 这不是一个尺取吗... 然后用multiset维护当前的区间,动态加, ...
- 「JOISC 2014 Day1」历史研究 --- 回滚莫队
题目又臭又长,但其实题意很简单. 给出一个长度为\(N\)的序列与\(Q\)个询问,每个询问都对应原序列中的一个区间.对于每个查询的区间,设数\(X_{i}\)在此区间出现的次数为\(Sum_{X_{ ...
- 「题解」「JOISC 2014 Day1」历史研究
目录 题目 考场思考 思路分析及标程 题目 点这里 考场思考 大概是标准的莫队吧,离散之后来一个线段树加莫队就可以了. 时间复杂度 \(\mathcal O(n\sqrt n\log n)\) . 然 ...
- 「JOISC 2020 Day4」首都城市
题目 点这里看题目. 分析 做法比较容易看出来.我们对于每个城市,找出那些 " 如果这个城市在首都内,则必须在首都内的其它城市 " ,也就是为了让这个城市的小镇连通而必须选 ...
- 「JOISC 2020 Day2」变态龙之色 题解
题目传送门 注意 同性必定不同色 必有一个同色异性,且不相互不喜欢 Solution 我们发现,我们问题比较大的就是如何确定性别问题.我们可以一个一个加进去,在原来已经确定了的二分图上增加新的性别关系 ...
随机推荐
- java的内存泄露是如何发生的,如何避免和发现
java的垃圾回收与内存泄露的关系:[新手可忽略不影响继续学习] 马克-to-win:上一节讲了,(i)对象被置成null.(ii)局部对象(无需置成null)当程序运行到右大括号.(iii)匿名对象 ...
- Spring Security的使用
spring security使用目的:验证,授权,攻击防护. 原理:创建大量的filter和interceptor来进行请求的验证和拦截,以此来达到安全的效果. Spring Security主要包 ...
- 用 JS(JavaScript )实现增删改查
JS小例题 学习内容: 需求 总结: 学习内容: 需求 用 JavaScript 实现简单增删改查 实现代码 <!DOCTYPE html PUBLIC "-//W3C//DTD HT ...
- 体验了一把最近很火的开源项目-MASA Blazor
前言 很惭愧直到去年底才接触到Blazor.那什么是Blazor呢?相信大家都看过官方文档的详细说明,另外MASA团队也有不错的说明介绍 .用官方的话说Blazor是一个交互式客户端Web UI的框架 ...
- Blazor组件自做二 : 使用JS隔离制作手写签名组件
Blazor组件自做二 : 使用JS隔离制作手写签名组件 本文相关参考链接 JavaScript 模块中的 JavaScript 隔离 Viewer.js工程 Blazor组件自做一 : 使用JS隔离 ...
- vim 下几种比较省劲的方式(vi结合着用)
Vim的几种模式 正常模式:可以使用快捷键命令,或按:输入命令行. 插入模式:可以输入文本,在正常模式下,按i.a.o等都可以进入插入模式. 可视模式:正常模式下按v可以进入可视模式, 在可视模式下, ...
- select下拉框获取下拉项值的问题
新发现: select option如果里面不写value值,默认提交<option></option>中间的值. 切记:真正提交的值写在value属性里面,option之间只 ...
- vue 项目build后部署上去页面空白
默认情况下vue-cli 会认为项目是部署在域名的根路径上. 但是当项目被部署到了一个子路径上,就要自己选定子路径. 比如项目被部署在了 https://www.ujapp.com/my-app, 则 ...
- go context详解
Context通常被称为上下文,在go中,理解为goroutine的运行状态.现场,存在上下层goroutine context的传递,上层goroutine会把context传递给下层gorouti ...
- 【虚拟机】VMware-Ubuntu-安装与卸载
VMware-Ubuntu 一.Ubuntu虚拟机安装 1.安装Ubuntu镜像 下载Ubuntu镜像文件,下载地址:http://www.ubuntu.com,点击download 下一步,到如下页 ...