這篇博客講 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 ↩︎