这篇博客讲 Codeforces Hello2020 的 D 题。
题目链接:
Hello2020 D. New Year and Conference
describe:
(懒得翻译了,打开网站或看照片把 QAQ
solution
1
先只考虑 a venue。如何快速判断出所有的 overlop?可以构建一个集合,以时间点(sa 和 ea 混在一起)为关键字排序,由前到后的顺序,有以下两个操作:
- 若该时刻有 lecture 开始,则将其加入集合。(该过程结束后,集合中所有 lectures 都 overlop。)
- 若该时刻有 lecture 结束,则让其离开集合。
如果我们在加入 a venue 的同时也把 对应 lecture 的 sb eb 也加进去了(由于集合是以 sa ea 排序的,所以 sb eb 很可能是乱序的),那么我们就可以由该时刻集合中所有 lectures 的 sbmax 与 ebmin 的关系判断 b venue 的 lectures 是否全部 overlop( 即 sbmax<=ebmin 时 overlop)。
sbmax 和 ebmin 的获得可由 <set> 中的 multiset 实现,单次操作复杂度为 O(logn)。
易知“任意时刻该集合中 b venue 都 overlop”是 venue-sensitive 的必要条件,我们记这样的结果为“成功”。一次该过程复杂度为 O(nlogn)。
若成功,我们就可以得到这样一个单向的结论:对于每对在 a venue 中 overlop 的 lectures,它们在 b venue 中也 overlop。
然后我们只需交换 a 和 b venue 的时间,再进行相同的过程,即可得到反向结论。
易知“两次过程都成功”是 venue-sensitive 的充要条件。总体复杂度为 O(nlogn)。
code
#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>
#include<queue>
#include<set>
using namespace std;
const int N=1e5+10;
struct Node
{
int as, ae, bs, be;
bool operator < (const Node &a) const { return ae>a.ae; }
}stu1[N], stu2[N];
bool cmp(Node a, Node b) { return a.as<b.as; }
int n;
int tim[N<<1];
priority_queue <Node> Q;
multiset<int> Ss, Se;
bool check(Node *stu)
{
int tn=0; Node sc;
sort(stu, stu+n, cmp);
for(int i=0; i<n; ++i) tim[i<<1]=stu[i].as, tim[i<<1|1]=stu[i].ae;
sort(tim, tim+n*2);
for(int i=1; i<n*2; ++i) if(tim[i]!=tim[tn]) tim[++tn]=tim[i];
tn++;
// 好像以上的步骤可以用一些神奇的函数轻易做到,但菜鸡我不会啊qwq
while(!Q.empty()) Q.pop();
Ss.clear(); Se.clear();
for(int i=0, cur=0, t; i<tn; ++i)
{
t=tim[i];
do
{
sc=stu[cur];
if(sc.as==t)
{
Q.push(sc);
cur++;
Ss.insert((-1)*sc.bs);//菜鸡我不知道怎么由大到小排序qwq
Se.insert(sc.be);
if(Q.size()>1&&(*Ss.begin())*(-1)>*Se.begin()) return false;
}
}while(sc.as==t && cur<n);
do
{
sc=Q.top();
if(sc.ae==t)
{
Q.pop();
Ss.erase((-1)*sc.bs);
Se.erase(sc.be);
}
}while(sc.ae==t&&!Q.empty());
}
return true;
}
int main()
{
//freopen("data.txt","r",stdin);
//freopen("my.txt","w",stdout);
scanf("%d", &n);
for(int i=0; i<n; ++i)
{
Node t;
scanf("%d%d%d%d", &t.as, &t.ae, &t.bs, &t.be);
stu1[i]=t; stu2[i]=(Node){t.bs,t.be,t.as,t.ae};
}
if(check(stu1)&&check(stu2)) puts("YES");
else puts("NO");
//fclose(stdout);
return 0;
}
another sulution:
2
从评论中翻出来了个用随机数的???竟然还被 hack 失败???我不明白为什么可以啊,谁能告诉我!!!
his code:
#include<bits/stdc++.h>
using namespace std;
struct seg{ int a, b, c, d; };
bool sort1(const seg& a, const seg& b){ return a.a < b.a; }
bool sort2(const seg& a, const seg& b){ return a.c < b.c; }
bool check(vector<seg>& c){
int n = c.size();
sort(c.begin(), c.end(), sort1);
long long check1 = 0;
for(int i = 0; i < n; i++){
//binary search to find right endpoint
int low = i, high = n-1, mid;
while(low < high){
mid = (low + high + 1)/2;
if(c[i].b >= c[mid].a){ //intersect
low = mid;
}else{
high = mid-1;
}
}
mid = (low + high + 1)/2;
//mid is the one which intersects
check1 += mid - i;
}
sort(c.begin(), c.end(), sort2);
long long check2 = 0;
for(int i = 0; i < n; i++){
//binary search to find right endpoint
int low = i, high = n-1, mid;
while(low < high){
mid = (low + high + 1)/2;
if(c[i].d >= c[mid].c){ //intersect
low = mid;
}else{
high = mid-1;
}
}
mid = (low + high + 1)/2;
//mid is the one which intersects
check2 += mid - i;
}
return check2 == check1;
}
signed main(){
ios::sync_with_stdio(false);
cin.tie(0);
srand(time(NULL));
int n; cin>>n;
vector<seg> a(n);
mt19937 rng;
for(int i = 0; i < n; i++){
cin>>a[i].a>>a[i].b>>a[i].c>>a[i].d;
}
for(int i = 0; i < 60; i++){
vector<seg> c;
c.reserve(n);
for(int j = 0; j < n; j++){
if(rng()%2 == 0){
c.push_back(a[j]);
}
}
bool ans = check(c);
if(!ans){
cout<<"NO"<<endl;
return 0;
}
}
cout<<"YES"<<endl;
}
reflect
①第一次用 multiset !
②如何识别两个数列的 py 关系(雾
③竟然可以把 s e 混合起来排序,果然混乱才能包容(?
最后的废话:
这次 cf 这题理解错了(其实理解对了也做不出来
菜鸡我只能做出3道(我已经很开心了
大一的我还什么都没有学,但已经在为了ACM的梦而努力。
如果真的有人看到了这篇博客(我的荣幸啊!),如果还能解决我的疑惑(比如 multiset 怎么升序排列,如何离散化,为什么可以随机的程序随机过),或是发现了什么问题,或是想到了其他东西,如果还想告诉我的话(真不容易啊),一定要告诉我啊(万分感谢),如果我能进校队的话,一定会很感激你的qwq。
怎么给目录设置链接啊啊啊aaa (感谢评论,我已经会了)
看一看他所谓的 randomised sol for D ↩︎