题解 [AGC015E] Mr.Aoki Incubator
让我先来口胡一个 \(O(n^2)\) 的也许能优化但就算能我也肯定懒得写的做法:
考虑补集转化,计算存在最后不被染黑的点的方案数
那么考虑枚举在所有点完成染色后最靠左的不被染黑的点
提前令一个 \(f_i\) 为区间 \([1, i]\) 所有点最后都能被染黑的染色方案数
那么考虑 \(i\) 的左侧有无初始为白色的点
若左侧不存在初始为白色的点,则首先需要 \(\forall j<i, v_j\leqslant v_i\)
然后考虑最靠右的 \(v_j<v_i\) 的点 \(j\),要求 \(\forall k\in[i, j], col_k=\text{white}\)
此时在 \(j\) 右侧的点可以随便染,即 \(\forall j\geqslant i, f_j\gets f_j+f_{i-1}\times2^{j-\max\{k\mid k\in[i, j]\and v_k<v_i\}}\)
这个貌似可以在权值线段树上对右侧所有 \(<v_i\) 的点做区间加减,然后差分实现
若左侧存在初始为白色的点,因为枚举的是剩下的第一个白点,
需要左侧 \(v_j>v_i\) 的点形成了一个元素间相邻的不降序列且这个序列与 \(i\) 相邻
然后考虑对右侧的贡献,若当前考虑对 \(f_j\) 的贡献:
令 \(k=max\{k\mid k\in[i, j]\and v_k<v_i\}\),\(t\) 为左侧 \(>v_i\) 的速度最小值,则要求 \([i, k]\) 均为白色且存在 \(v\in(v_i, t)\) 的黑色点
那么容斥算 \(v\in(v_i, t)\) 的点最后均为白色的方案数是容易的
就是要求每个点前面都不能有比它快的黑色点,后面也不能有比它慢的黑色点
如果枚举 \(k\) 的位置的话就是每次向末尾加点,应该是可以优化到 \(O(n^2)\) 的
然后正解:
其实差不多吧
先进行一些观察,发现涂黑一个 \(i\) 的影响是涂黑了
前面的越过 \(i\) 会染黑后面的,于是其实涂黑了
那么发现 \(i\) 越大,对右边的限制越强
那么考虑枚举初始涂黑的最靠右的点,要求 \(\tt sufmax_{i+1}<premax_i\)
令 \(f_i\) 为在 \(i\) 染色条件下,将 \([1, i-1]\) 中 \(v\leqslant \tt sufmax_i\) 的点染色的方案数
那么
发现 \(\max\limits_{k<i}\{v_k\mid v_k\leqslant sufmin_i\}\) 是单调的,于是对顶堆维护出来
发现此时合法的 \(j\) 是一个以 \(i\) 为右端点的区间,单调指针维护出来即可
复杂度 \(O(n\log n)\)
点击查看代码
#include <bits/stdc++.h>
using namespace std;
#define INF 0x3f3f3f3f
#define N 200010
#define fir first
#define sec second
#define ll long long
//#define int long long
char buf[1<<21], *p1=buf, *p2=buf;
#define getchar() (p1==p2&&(p2=(p1=buf)+fread(buf, 1, 1<<21, stdin)), p1==p2?EOF:*p1++)
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;
const ll mod=1e9+7;
pair<int, int> a[N];
ll f[N], sum[N], ans;
int premin[N], premax[N], sufmin[N], sufmax[N];
priority_queue<int, vector<int>, less<int>> q1;
priority_queue<int, vector<int>, greater<int>> q2;
signed main()
{
n=read();
for (int i=1; i<=n; ++i) a[i].fir=read(), a[i].sec=read();
sort(a+1, a+n+1);
premin[0]=sufmin[n+1]=INF;
for (int i=1; i<=n; ++i) premin[i]=min(premin[i-1], a[i].sec);
for (int i=1; i<=n; ++i) premax[i]=max(premax[i-1], a[i].sec);
for (int i=n; i; --i) sufmin[i]=min(sufmin[i+1], a[i].sec);
for (int i=n; i; --i) sufmax[i]=max(sufmax[i+1], a[i].sec);
f[0]=sum[0]=1;
q1.push(0);
int pos=0;
for (int i=1; i<=n; ++i) {
// cout<<"i: "<<i<<endl;
// cout<<"pos: "<<pos<<endl;
// cout<<"mx: "<<q1.top()<<endl;
while (premax[pos]<q1.top()) ++pos;
f[i]=(sum[i-1]-(pos?sum[pos-1]:0))%mod;
sum[i]=(sum[i-1]+f[i])%mod;
q2.push(a[i].sec);
while (q2.size() && q2.top()<sufmin[i+1]) q1.push(q2.top()), q2.pop();
if (sufmax[i+1]<premax[i]) ans=(ans+f[i])%mod;
}
printf("%lld\n", (ans%mod+mod)%mod);
return 0;
}