「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 我们发现,我们问题比较大的就是如何确定性别问题.我们可以一个一个加进去,在原来已经确定了的二分图上增加新的性别关系 ...
随机推荐
- snippet,让你编码效率翻倍
为什么谈到Snippet 今天下午在用vscode做小程序的时候,发现很不方便,因为商店里提供的代码片段极为有限,而且平时几乎每天都需要用到代码片段,所以就在思考他们是怎么做到给别人提供代码的,我可以 ...
- Hadoop真分布式实现SSH免密登录
首先需要两台服务器(CentOS安装Linux见:https://www.cnblogs.com/syq816/p/12355115.html) 伪分布式的搭建见:https://www.cnblog ...
- jboss7学习2-jboss7入门(端口和访问的ip问题)
1.下载地址: http://www.jboss.org/jbossas/downloads ,下载Certified Java EE 6 Full Profile版本. 2.解压 jboss-as- ...
- 解决vscode开发react项目没有html标签自动补全
怎么设置自动补全: 左下角齿轮按钮--设置-- 在搜索框中输入Include Languages,选择Emmet,并在Emmet:include language下点击在setting.json中编辑 ...
- numpy教程03---ndarray的运算
欢迎关注公众号[Python开发实战], 获取更多内容! 工具-numpy numpy是使用Python进行数据科学的基础库.numpy以一个强大的N维数组对象为中心,它还包含有用的线性代数,傅里叶变 ...
- 在原有mysql机器上增加一台实例
采用的是yum install mysql-community-server yum方式安装mysql(社区版) 文章基础上新加一个mysql实例. 这个完全可以直接实战上应用,只要规划好即可 服务器 ...
- JavaWeb学习day3-Maven&安装
1.官网下载:https://maven.apache.org/ 2.解压下载好的压缩包 3.配置环境变量 添加如下图变量 在path变量下添加下图 4.安装完成检测 cmd输入:mvn -versi ...
- Blazor使用区域(Area)
在MVC中,我们经常使用区域(Area)来区分各个模块,比如后台我们可以写一个Admin的Area. 到了Blazor时代,已经不推荐这么做了,现在推荐的做法是通过Url来区分,比如Admin可以配置 ...
- Sentinel基础应用
Sentinel 是什么? 随着微服务的流行,服务和服务之间的稳定性变得越来越重要.Sentinel 以流量为切入点,从流量控制.熔断降级.系统负载保护等多个维度保护服务的稳定性. Sentinel ...
- 多线程的创建,并发,静态代理,Lambda表达式
程序是指令和数据的有序集合,本身没有任何运行的含义.是一个静态的概念. 在操作系统中运行的程序就是进程(Process),如:QQ,播放器,游戏等等. 进程是程序的一次执行过程,是一个动态的概念,是系 ...