叁仟柒佰万
- problem
- solution
- code(50’)
- code(90’)
- code(100’)
problem
多组数据。
给定一个序列 aaa,你可以将它划分成任意多段,满足每一个段的 mex
相同。
求方案数,对 109+710^9+7109+7 取模。
T≤10,n≤3e5T\le 10,n\le 3e5T≤10,n≤3e5。
另有一组数据 T=1,n=3e7T=1,n=3e7T=1,n=3e7。
solution
observation
:最后合法划分方案的 mex
一定是相同的。
在考场上,没有必要去仔细证明,想想就是。出现过的数一定不可能成为
mex
。就从最小的 000 考虑,当它出现时,必定会让最后答案的
mex
>0>0>0,因为不管这个 000 出现在哪里,那个段的mex
都不能是 000。令全局 mexmexmex 为 kkk,则说明 0∼k−10\sim k-10∼k−1 的数都存在于数列中而 kkk 不存在。
假设最后每个区间的 mexmexmex 均为 xxx
- 若 x<kx<kx<k,由于序列中为 xxx 的数存在,xxx 必定在其中一个区间中,与所有区间 mex=xmex=xmex=x 矛盾。
- 若 x>kx>kx>k,由于不存在 kkk,显然不合法。
对于 30%30\%30% 的数据 n≤100n\le 100n≤100,是属于基础暴力部分分的。
直接列 dpi,j:dp_{i,j}:dpi,j: 到第 iii 个数划分了 jjj 段的方案数。
枚举上一个数的划分点 dpk−1,j−1→dpi,jdp_{k-1,j-1}\rightarrow dp_{i,j}dpk−1,j−1→dpi,j。其中要满足 [k,i][k,i][k,i] 一段的 mex
是与前一个相同的。
但是仔细想想,发现无非就是 fk−1→fif_{k-1}\rightarrow f_{i}fk−1→fi,具体分了多少段其实并不需要记录,因为题目没有对段数进行限制。
这样就将第二维去掉了,时间复杂度就去了一个 O(n)O(n)O(n)。
问题就来到怎么处理一段区间内的 mex
。这不禁让我想到了之前做过的一道题👉链接。
对 0∼n0\sim n0∼n 建立权值线段树,对于每个 iii,将 aia_iai 对应线段树上叶子的权值置为 iii。
从左往右做,每次直接查询 [0,mex)[0,mex)[0,mex) 区间内的最小值,就是最大的小于 iii 的满足条件的 kkk。
你会马上反应过来,所有 k′≤kk'\le kk′≤k 的 k′k'k′ 都满足这样的条件了,都会对 dpi,jdp_{i,j}dpi,j 产生贡献。
每次,你可以线段树 O(logn)O(logn)O(logn) 求出 kkk 后再枚举 k′k'k′,时间复杂度为 O(Tnlogn+Tn2)O(Tn\log n+Tn^2)O(Tnlogn+Tn2),针对 50%50\%50% 的数据。
马上就知道,这是一个前缀和优化的过程。时间复杂度又可以将为 O(Tnlogn)O(Tn\log n)O(Tnlogn),针对 90%90\%90% 的数据。
所以这道题只要会求 mex
的那个线段树技巧,拿到 90′90'90′ 的高分已经很不错了,近乎是将这道题 ACACAC。
最后就是 O(n)O(n)O(n) 的正解。
observation
:对于同一个右端点,其左端点对应的 mex
是单调的。【上面已经有所体现,这里只是规范化的表达】
所以可以用双指针和桶维护区间内的 mex
是否是全局的 mex(k)
即可。【具体可看代码实现】
code(50’)
#include <bits/stdc++.h>
using namespace std;
#define maxn 3005
#define int long long
#define mod 1000000007
#define inf 0x7f7f7f7f
int T, n;
bool vis[maxn];
int a[maxn], f[maxn], t[maxn << 2];#define lson now << 1
#define rson now << 1 | 1
#define mid ( ( l + r ) >> 1 )void build( int now, int l, int r ) {t[now] = 0;if( l == r ) return;build( lson, l, mid );build( rson, mid + 1, r );
}void modify( int now, int l, int r, int pos, int k ) {if( l == r ) { t[now] = k; return; }if( pos <= mid ) modify( lson, l, mid, pos, k );else modify( rson, mid + 1, r, pos, k );t[now] = min( t[lson], t[rson] );
}int query( int now, int l, int r, int L, int R ) {if( R < l or r < L ) return inf;if( L <= l and r <= R ) return t[now];return min( query( lson, l, mid, L, R ), query( rson, mid + 1, r, L, R ) );
}signed main() {freopen( "clods.in", "r", stdin );freopen( "clods.out", "w", stdout );scanf( "%lld", &T );while( T -- ) {memset( f, 0, sizeof( f ) );memset( vis, 0, sizeof( vis ) );scanf( "%lld", &n ); int x;for( int i = 1;i <= n;i ++ ) scanf( "%lld", &a[i] ), vis[a[i]] = 1;for( int i = 0;i <= n;i ++ ) if( ! vis[i] ) { x = i; break; }f[0] = 1;build( 1, 0, n );for( int i = 1;i <= n;i ++ ) {modify( 1, 0, n, a[i], i );int k = query( 1, 0, n, 0, x - 1 );if( k == inf and x ) continue;if( k == inf ) k = i;for( int j = 0;j < k;j ++ ) f[i] = ( f[i] + f[j] ) % mod;}printf( "%lld\n", f[n] );}return 0;
}
code(90’)
#include <bits/stdc++.h>
using namespace std;
#define maxn 300005
#define int long long
#define mod 1000000007
#define inf 0x7f7f7f7f
int T, n;
bool vis[maxn];
int a[maxn], f[maxn], g[maxn], t[maxn << 2];#define lson now << 1
#define rson now << 1 | 1
#define mid ( ( l + r ) >> 1 )void build( int now, int l, int r ) {t[now] = 0;if( l == r ) return;build( lson, l, mid );build( rson, mid + 1, r );
}void modify( int now, int l, int r, int pos, int k ) {if( l == r ) { t[now] = k; return; }if( pos <= mid ) modify( lson, l, mid, pos, k );else modify( rson, mid + 1, r, pos, k );t[now] = min( t[lson], t[rson] );
}int query( int now, int l, int r, int L, int R ) {if( R < l or r < L ) return inf;if( L <= l and r <= R ) return t[now];return min( query( lson, l, mid, L, R ), query( rson, mid + 1, r, L, R ) );
}const int Mxdt=100000;
inline char gc(){static char buf[Mxdt],*p1=buf,*p2=buf;return p1==p2&&(p2=(p1=buf)+fread(buf,1,Mxdt,stdin),p1==p2)?EOF:*p1++;
}
inline int read(){int t=0,f=0;char v=gc();while(v<'0')f|=(v=='-'),v=gc();while(v>='0')t=(t<<3)+(t<<1)+v-48,v=gc();return f?-t:t;
}signed main() {freopen( "clods.in", "r", stdin );freopen( "clods.out", "w", stdout );T = read();while( T -- ) {memset( f, 0, sizeof( f ) );memset( vis, 0, sizeof( vis ) );n = read(); int x;for( int i = 1;i <= n;i ++ ) a[i] = read(), vis[a[i]] = 1;for( int i = 0;i <= n;i ++ ) if( ! vis[i] ) { x = i; break; }g[0] = 1;build( 1, 0, n );for( int i = 1;i <= n;i ++ ) {modify( 1, 0, n, a[i], i );int k = query( 1, 0, n, 0, x - 1 );if( k == inf and x ) continue;if( k == inf ) k = i;f[i] = g[k - 1];g[i] = ( g[i - 1] + f[i] ) % mod;}printf( "%lld\n", f[n] );}return 0;
}
code(100’)
#include <bits/stdc++.h>
using namespace std;
#define mod 1000000007
#define ll long long
#define maxn 37000005
int n, T;
int a[maxn], f[maxn], vis[maxn];const int Mxdt=100000;
inline char gc(){static char buf[Mxdt],*p1=buf,*p2=buf;return p1==p2&&(p2=(p1=buf)+fread(buf,1,Mxdt,stdin),p1==p2)?EOF:*p1++;
}
inline int read(){int t=0,f=0;char v=gc();while(v<'0')f|=(v=='-'),v=gc();while(v>='0')t=(t<<3)+(t<<1)+v-48,v=gc();return f?-t:t;
}int main() {freopen( "clods.in", "r", stdin );freopen( "clods.out", "w", stdout );T = read();while( T -- ) {n = read();if( n == 37000000 ) {int x = read(), y = read();for( int i = 2;i <= n;i ++ ) vis[a[i] = ( 1ll * a[i - 1] * x + y + i ) & 262143] = 1; }elsefor( int i = 1;i <= n;i ++ ) vis[a[i] = read()] = 1;int mex;for( int i = 0;i <= n;i ++ ) if( ! vis[i] ) { mex = i; break; }for( int i = 0;i <= n;i ++ ) vis[i] = f[i] = 0; int pos = 0;for( int i = 1;i <= n;i ++ ) {vis[a[i]] ++;while( vis[pos] ) pos ++;if( pos >= mex ) { pos = i; break; }}int sum = 1, now = 1; -- vis[a[pos]];for( int i = pos;i <= n;i ++ ) {++ vis[a[i]];while( now < i and ( a[now] > mex or vis[a[now]] > 1 ) ) {sum = ( 1ll * sum + f[now] ) % mod;-- vis[a[now]];++ now;}f[i] = sum;}printf( "%d\n", f[n] );for( int i = 0;i <= n;i ++ ) vis[i] = 0;}return 0;
}