叁仟柒佰万(mex+线段树+dp+前缀和优化+双指针+桶)
叁仟柒佰萬
- 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(Tnlog?n+Tn2)O(Tn\log n+Tn^2)O(Tnlogn+Tn2),針對 50%50\%50% 的數據。
馬上就知道,這是一個前綴和優化的過程。時間復雜度又可以將為 O(Tnlog?n)O(Tn\log n)O(Tnlogn),針對 90%90\%90% 的數據。
所以這道題只要會求 mex 的那個線段樹技巧,拿到 90′90'90′ 的高分已經很不錯了,近乎是將這道題 ACACAC。
最后就是 O(n)O(n)O(n) 的正解。
observation:對于同一個右端點,其左端點對應的 mex 是單調的?!旧厦嬉呀浻兴w現,這里只是規范化的表達】
所以可以用雙指針和桶維護區間內的 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; }總結
以上是生活随笔為你收集整理的叁仟柒佰万(mex+线段树+dp+前缀和优化+双指针+桶)的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 自己怎么做网站啊(如何自己制作网站)
- 下一篇: 织梦怎么调用栏目内容(织梦调用栏目名称)