[JOISC 2017 Day1] 港湾设备 题解
模拟赛赛时想到的一种很简洁的做法,好想好写且只需要使用 std::set,故在此记录一下。
令
进一步地,对于每个集装箱,考虑其“在港时间”即
考虑优化这个过程——这一些满足条件的
均摊一下上述所有的操作只会进行 std::set 维护,所以时间复杂度为
放代码:
#include<bits/stdc++.h>
using namespace std;
typedef pair<int,int> pii;
const int p=1e9+7;
inline int qpow(int a,int b){
int r=1;
while(b){
if(b&1)r=1ll*r*a%p;
a=1ll*a*a%p,b>>=1;
}
return r;
}
int main(){
ios::sync_with_stdio(false);
cin.tie(0); cout.tie(0);
int n,q=0; cin>>n;
vector<pii> a(n);
for(auto &e:a){
cin>>e.first>>e.second;
e.first--,e.second--;
}
sort(a.begin(),a.end());
set<pii> s1,s2;
/* s1 为所有可以留在场上的元素:
换句话说,就是包含了所有在“挑选”过程中保留或者没保留的所有元素;
它的作用与上文中所述的“容器 s”的作用相同。
s2 为所有的代表元和还没有被连边的元素。
*/
vector<vector<int> > g(n);
for(int i=0;i<n;i++){
int l,r; tie(l,r)=a[i];
while(!s1.empty()&&s1.begin()->first<l)s1.erase(s1.begin());
// 清空容器 s 中应该被“剔除”的元素
while(!s2.empty()&&s2.begin()->first<l){
auto p=s1.lower_bound(*s2.begin());
if(p!=s1.end())s2.emplace(*p);
s2.erase(s2.begin());
} // “剔除”代表元的过程,要从 s1 中找后继
auto p=s2.begin();
if(p!=s2.end()&&p->first<r){
int ps=p->second;
g[i].emplace_back(ps);
g[ps].emplace_back(i);
for(auto it=next(p);it!=s2.end()&&it->first<r;it=s2.erase(it)){
g[i].emplace_back(it->second);
g[it->second].emplace_back(i);
} // “挑选”过程,暴力连边,并删除非代表元
}
s1.emplace(r,i),s2.emplace(r,i); // 将其加入
}
vector<int> c(n,-1);
auto dfs=[&](auto &&self,int u)->void{
for(int i:g[u]){
if(c[i]<0)c[i]=!c[u],self(self,i);
else if(c[i]==c[u])cout<<"0\n",exit(0);
}
}; // 二分图染色
for(int i=0;i<n;i++)
if(c[i]<0)c[i]=0,dfs(dfs,i),q++;
cout<<qpow(2,q)<<endl;
return 0;
}