题解 [CSP-S 2021] 廊桥分配
初看暴力非常好打
细看貌似比较套路,处理出数组 \(f[i]\) 和 \(g[i]\) 表示两种位置分别分配 \(i\) 个廊桥时的最大停靠数
然后 \(O(n)\) 合并
发现一个事情是一架飞机如果能在 \(i\) 的情况下产生贡献,也就一定能在 \(i+1\) 的情况下产生贡献(其实假了)
于是尝试维护一个偏移量dlt表示前dlt个廊桥已被占满,然后每次给 \([dlt+1, n]\) 加上1
其实显然假了,因为前面那个结论就是假的连样例都过不去
然后有想过三分,暴力跑了下样例发现不是单峰又跑路了
然后又发现一个性质:\(i\) 的情况能选的飞机一定是 \(i+1\) 能选的飞机的子集
其实正解到这里就显然了,但我想偏了
因为我暴力是差分实现的,所以到这里就觉得 \(i\) 的情况下会有一些时段有些廊桥是空着的,会对 \(i+1\) 造成影响
其实并不会,贪心可以显然证明如果有一架飞机必须在 \(i\) 时刻及以后才能加入,则一定有某一时刻有 \(i\) 架
可以画图证明所想的情况不成立,即希望会被影响到的飞机其实可以占用标号更小的廊桥
于是枚举廊桥,贪心占满它
可以用一个set维护仍然没有停靠的飞机
复杂度 \(O(nlogn)\)
Code:
#include <bits/stdc++.h>
using namespace std;
#define INF 0x3f3f3f3f
#define N 400010
#define ll long long
#define fir first
#define sec second
#define make make_pair
//#define int long long
inline int read() {
int ans=0, f=1; char c=getchar();
while (!isdigit(c)) {if (c=='-') f=-f; c=getchar();}
while (isdigit(c)) {ans=(ans<<3)+(ans<<1)+(c^48); c=getchar();}
return ans*f;
}
int n, m1, m2;
int uni[N], usize;
pair<int, int> p1[N], p2[N];
namespace force{
int ans;
int op[N];
int check1(int k) {
// memset(op, 0, sizeof(op));
for (int i=0; i<=usize; ++i) op[i]=0;
int pos=1, cnt=0;
for (int i=1; i<=usize; ++i) {
k+=op[i];
while (pos<=m1 && p1[pos].fir==i) {
if (k) --k, ++cnt, ++op[p1[pos].sec];
++pos;
}
}
return cnt;
}
int check2(int k) {
// memset(op, 0, sizeof(op));
for (int i=0; i<=usize; ++i) op[i]=0;
int pos=1, cnt=0;
for (int i=1; i<=usize; ++i) {
k+=op[i];
while (pos<=m2 && p2[pos].fir==i) {
if (k) --k, ++cnt, ++op[p2[pos].sec];
++pos;
}
}
return cnt;
}
void solve() {
for (int i=0; i<=n; ++i) ans=max(ans, check1(i)+check2(n-i)); //, cout<<check1(i)+check2(n-i)<<' '; cout<<endl;
// cout<<"f: "; for (int i=1; i<=n; ++i) cout<<check1(i)<<' '; cout<<endl;
// cout<<"g: "; for (int i=1; i<=n; ++i) cout<<check2(i)<<' '; cout<<endl;
printf("%d\n", ans);
exit(0);
}
}
namespace task1{
int f[N], g[N], op[N], ans;
void solve() {
int dlt, pos;
dlt=0; pos=1;
memset(op, 0, sizeof(op));
for (int i=1; i<=usize; ++i) {
dlt+=op[i];
cout<<"i: "<<i<<' '<<dlt<<endl;
while (pos<=m1 && p1[pos].fir==i) {
--op[p1[pos].sec];
cout<<p1[pos].sec<<endl;
for (int j=dlt+1; j<=n; ++j) ++f[j];
++dlt; ++pos;
}
}
dlt=0; pos=1;
memset(op, 0, sizeof(op));
for (int i=1; i<=usize; ++i) {
dlt+=op[i];
while (pos<=m2 && p2[pos].fir==i) {
--op[p2[pos].sec];
for (int j=dlt+1; j<=n; ++j) ++g[j];
++dlt; ++pos;
}
}
cout<<"f: "; for (int i=1; i<=n; ++i) cout<<f[i]<<' '; cout<<endl;
cout<<"g: "; for (int i=1; i<=n; ++i) cout<<g[i]<<' '; cout<<endl;
for (int i=0; i<=n; ++i) ans=max(ans, f[i]+g[n-i]);
printf("%d\n", ans);
exit(0);
}
}
namespace task{
int f[N], g[N], ans;
set<pair<int, int>> s;
void solve() {
set<pair<int, int>>::iterator it;
for (int i=1; i<=m1; ++i) s.insert(p1[i]);
for (int i=1; i<=n; ++i) if (s.size()) {
int now=0;
while ((it=s.upper_bound(make(now, 0)))!=s.end()) {
++f[i];
now=it->sec;
s.erase(it);
}
f[i]+=f[i-1];
}
s.clear();
for (int i=1; i<=m2; ++i) s.insert(p2[i]);
for (int i=1; i<=n; ++i) if (s.size()) {
int now=0;
while ((it=s.upper_bound(make(now, 0)))!=s.end()) {
++g[i];
now=it->sec;
s.erase(it);
}
g[i]+=g[i-1];
}
for (int i=0; i<=n; ++i) ans=max(ans, f[i]+g[n-i]);
printf("%d\n", ans);
exit(0);
}
}
signed main()
{
freopen("airport.in", "r", stdin);
freopen("airport.out", "w", stdout);
n=read(); m1=read(); m2=read();
for (int i=1,a,b; i<=m1; ++i) {
a=read(); b=read();
p1[i]=make(a, b);
uni[++usize]=a; uni[++usize]=b;
}
for (int i=1,a,b; i<=m2; ++i) {
a=read(); b=read();
p2[i]=make(a, b);
uni[++usize]=a; uni[++usize]=b;
}
sort(uni+1, uni+usize+1);
usize=unique(uni+1, uni+usize+1)-uni-1;
for (int i=1; i<=m1; ++i) {
p1[i].fir=lower_bound(uni+1, uni+usize+1, p1[i].fir)-uni;
p1[i].sec=lower_bound(uni+1, uni+usize+1, p1[i].sec)-uni;
}
for (int i=1; i<=m2; ++i) {
p2[i].fir=lower_bound(uni+1, uni+usize+1, p2[i].fir)-uni;
p2[i].sec=lower_bound(uni+1, uni+usize+1, p2[i].sec)-uni;
}
sort(p1+1, p1+m1+1); sort(p2+1, p2+m2+1);
// force::solve();
// task1::solve();
task::solve();
return 0;
}