题解 叁仟柒佰万

传送门

\(min\{a_i\}>0\) 时,整个序列可以被任意划分
否则可以证明原序列的mex一定是划分得到的序列的mex
于是令 \(f[i]\) 为区间 \([1, i]\) 的合法划分方案数
特别的,\(f[0]=1\)
于是发现转移点 \(j\) 要求 \([0, mex)\) 中的数在区间 \((j, i]\) 中都出现过
于是可以对值域维护一个 \(lst[i]\) 表示 \(i\) 这个数上一次出现的位置
最终决策点就是 \(min\{lst_i\}-1\)
这个东西可以用堆维护,但会带个log
有没有更优的方案呢?
发现随着i的增加,最终决策点j是单调不降的
于是用一个指针+桶维护这个决策点就可以优化到 \(O(n)\)

Code:
#include <bits/stdc++.h>
using namespace std;
#define INF 0x3f3f3f3f
#define N 300010
#define ll long long
#define fir first
#define sec second
#define make make_pair
//#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;
int a[N], minn;
ll f[N], g[N];
bool vis[N];
const ll mod=1e9+7;
inline void md(ll& a, ll b) {a+=b; a=a>=mod?a-mod:a;}
inline ll md(ll a) {return a>=mod?a-mod:a;}

namespace task1{
	int lst[N], mex;
	bool vis[N];
	void solve() {
		if (minn>0) {printf("%lld\n", g[n]); return ;}
		memset(f, 0, sizeof(f));
		memset(lst, 0, sizeof(lst));
		memset(vis, 0, sizeof(vis));
		for (int i=1; i<=n; ++i) vis[a[i]]=1;
		for (int i=0; i<=n; ++i) if (!vis[i]) {mex=i; break;}
		f[0]=1;
		for (int i=1; i<=n; ++i) {
			lst[a[i]]=i;
			int mini=INF;
			for (int j=0; j<mex; ++j) mini=min(mini, lst[j]);
			for (int j=0; j<mini; ++j) f[i]=(f[i]+f[j])%mod;
		}
		printf("%lld\n", f[n]);
	}
}

namespace task2{
	int lst[N], mex;
	ll sum[N];
	priority_queue<pair<int, int>, vector<pair<int, int>>, greater<pair<int, int>>> q;
	void solve() {
		if (minn>0) {printf("%lld\n", g[n]); return ;}
		for (int i=0; i<=n; ++i) vis[i]=0;
		while (q.size()) q.pop();
		for (int i=1; i<=n; ++i) vis[a[i]]=1;
		for (int i=0; i<=n; ++i) if (!vis[i]) {mex=i; break;}
		sum[0]=f[0]=1;
		for (int i=0; i<mex; ++i) q.push(make(0, i)), lst[i]=0;
		for (int i=1; i<=n; ++i) {
			f[i]=0;
			lst[a[i]]=i;
			if (a[i]<mex) q.push(make(i, a[i]));
			while (1) {
				if (lst[q.top().sec]==q.top().fir) break;
				else q.pop();
			}
			// cout<<"mini: "<<q.top().fir<<' '<<q.top().sec<<' '<<lst[q.top().sec]<<endl;
			if (q.top().fir>0) f[i]=sum[q.top().fir-1];
			sum[i]=md(sum[i-1]+f[i]);
		}
		printf("%lld\n", f[n]);
	}
}

namespace task{
	#define N2 37000010
	int f[N2], buc[N2], lst[N2], mex;
	bool vis[N2];
	void solve() {
		// cout<<double(sizeof(a)*3+sizeof(vis))/1024/1024<<endl;
		ll x=read(), y=read();
		vis[0]=1;
		for (int i=2,t=0; i<=n; ++i) vis[t=(t*x+y+i)&262143]=1;
		// cout<<"a: "; for (int i=1; i<=n; ++i) cout<<a[i]<<' '; cout<<endl;
		for (int i=0; i<=n; ++i) if (!vis[i]) {mex=i; break;}
		f[0]=1;
		for (int i=0; i<mex; ++i) ++buc[lst[i]=0];
		int pos=0;
		for (int i=1,t; i<=n; ++i) {
			if (i==1) t=0;
			else t=(t*x+y+i)&262143;
			if (t<mex) {
				--buc[lst[t]];
				++buc[lst[t]=i];
			}
			while (!buc[pos]) ++pos;
			if (pos>0) f[i]=f[pos-1];
			f[i]=(f[i-1]+f[i])%mod;
		}
		printf("%lld\n", ((f[n]-f[n-1])%mod+mod)%mod);
		exit(0);
	}
}

signed main()
{
	freopen("clods.in", "r", stdin);
	freopen("clods.out", "w", stdout);

	int T=read();
	g[0]=g[1]=1; ll sum=2;
	for (int i=2; i<N; ++i) {g[i]=sum; md(sum, g[i]);}
	while (T--) {
		n=read(); minn=INF;
		if (n==37000000) task::solve();
		for (int i=1; i<=n; ++i) {
			a[i]=read();
			minn=min(minn, a[i]);
		}
		task2::solve();
	}

	return 0;
}
posted @ 2021-11-11 06:21  Administrator-09  阅读(0)  评论(0编辑  收藏  举报