题解 [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\) 的影响是涂黑了

\[\forall j>i, v_j<v_i \]

\[\forall j<i, v_j>v_i \]

前面的越过 \(i\) 会染黑后面的,于是其实涂黑了

\[\forall j>i, v_j<\max\limits_{k\leqslant i}v_k \]

\[\forall j<i, v_j>\min\limits_{k\geqslant i}v_k \]

那么发现 \(i\) 越大,对右边的限制越强
那么考虑枚举初始涂黑的最靠右的点,要求 \(\tt sufmax_{i+1}<premax_i\)
\(f_i\) 为在 \(i\) 染色条件下,将 \([1, i-1]\)\(v\leqslant \tt sufmax_i\) 的点染色的方案数
那么

\[f_i=\sum\limits_{j<i}[\nexists k\in(j, i), premax_j\leqslant v_k \leqslant sufmin_i]f_j \]

发现 \(\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;
}
posted @ 2022-05-25 20:44  Administrator-09  阅读(2)  评论(0编辑  收藏  举报