线段树/扫描线问卷调查反馈——Rmq Problem / mex(主席树),Boring Queries(二分+st表+主席树),Colorful Squares(扫描线)
文章目錄
- Rmq Problem / mex
- Boring Queries
- Colorful Squares
Rmq Problem / mex
luogu4137
對aia_iai?建權值線段樹
再加上可持久化
對于第RRR個版本的線段樹,每個葉子xxx存的是ai=xa_i=xai?=x的所有iii中最大的小于RRR的位置iii
那么詢問[L,R][L,R][L,R]就轉化成RRR版本線段樹中所有小于LLL的葉子中的最小值
#include <cstdio> #include <iostream> using namespace std; #define maxn 200005 int n, Q, cnt; int a[maxn], rt[maxn]; struct node { int l, r, Min; }t[maxn * 50];void modify( int lst, int &now, int l, int r, int val, int pos ) {if( ! now ) now = ++ cnt;if( l == r ) { t[now].Min = pos; return; }int mid = ( l + r ) >> 1;if( val <= mid ) {t[now].r = t[lst].r;modify( t[lst].l, t[now].l, l, mid, val, pos );}else {t[now].l = t[lst].l;modify( t[lst].r, t[now].r, mid + 1, r, val, pos );}t[now].Min = min( t[t[now].l].Min, t[t[now].r].Min ); }int query( int now, int l, int r, int ti ) {if( l == r ) return l;int mid = ( l + r ) >> 1;if( t[t[now].l].Min <= ti ) return query( t[now].l, l, mid, ti );else return query( t[now].r, mid + 1, r, ti ); }int main() {scanf( "%d %d", &n, &Q );for( int i = 1;i <= n;i ++ ) scanf( "%d", &a[i] );for( int i = 1;i <= n;i ++ ) {a[i] = min( a[i], n );modify( rt[i - 1], rt[i], 0, n, a[i], i );}for( int i = 1, l, r;i <= Q;i ++ ) {scanf( "%d %d", &l, &r );printf( "%d\n", query( rt[r], 0, n, l - 1 ) );}return 0; }Boring Queries
CF1422F
ai∈[1,2e5]→a_i\in[1,2e5]\rightarrowai?∈[1,2e5]→ aia_iai?分解質因子后,最多只會有一個>2e5≈450>\sqrt{2e5}≈450>2e5?≈450的質因數
而在450內的質因數有87個
所以按質因數大小分塊
預處理aia_iai?含有的小于450的質因數primej\rm prime_jprimej?的個數
lcm\rm lcmlcm可以表示為∏max?lrpi\prod\max_l^rp_i∏maxlr?pi?(每一個質因數的個數最大值的乘積)
對于每個質因數開一個ststst表,開878787個就行,預處理出該質因數在a[l,r]a[l,r]a[l,r]內出現次數的最大值
剩下的一個>450>450>450的質因數,利用線段樹來維護區間[l,r][l,r][l,r]內所有這些大質因數乘積
- 如果沒有就往里面扔111
- 如果有
- 考慮對于區間[l,r][l,r][l,r]內多次出現的xxx,只能統計一次
- 記prei:aipre_i:a_iprei?:ai?上一次出現的位置
- 只在區間[l,r][l,r][l,r]的xxx第一次出現的時候把xxx統計進答案
- 這些位置的特點是prei≤l?1pre_i\le l-1prei?≤l?1
- 所以相當于?l≤i≤r∏i[prei≤l?1]ai\forall_{l\le i\le r} \prod_i\Big[pre_i\le l-1\Big]a_i?l≤i≤r?∏i?[prei?≤l?1]ai?
- 主席樹維護,rt[r]rt[r]rt[r]的葉子維護該值距離rrr最近的位置
- 當時候直接查rt[l?1]rt[l-1]rt[l?1]就行
跟第一題很類似的主席樹維護
#include <cmath> #include <cstdio> #include <iostream> using namespace std; #define mod 1000000007 #define maxn 200005 struct node { int l, r, val; }t[maxn * 20]; int n, Q, cnt, tot; bool vis[455]; int prime[90], a[maxn], rt[maxn], nxt[maxn], pos[maxn]; int mi[90][20]; char st[90][maxn][20];void sieve() {for( int i = 2;i <= 450;i ++ ) {if( ! vis[i] ) prime[++ cnt] = i;for( int j = 1;j <= cnt and i * prime[j] <= 450;j ++ ) {vis[i * prime[j]] = 1;if( i % prime[j] == 0 ) break;}} }void build() {for( int k = 1;k <= cnt;k ++ )for( int j = 1;j < 20;j ++ )for( int i = 1;i + ( 1 << j ) - 1 <= n;i ++ )st[k][i][j] = max( st[k][i][j - 1], st[k][i + ( 1 << j - 1 )][j - 1] ); }int query( int k, int l, int r ) {int i = log2( r - l + 1 );return max( st[k][l][i], st[k][r - ( 1 << i ) + 1][i] ); }void build( int &now, int l, int r ) {if( ! now ) now = ++ tot;t[now].val = 1;if( l == r ) return;int mid = l + r >> 1;build( t[now].l, l, mid );build( t[now].r, mid + 1, r ); }void modify( int now, int l, int r, int pos, int val ) {if( l == r ) { t[now].val = val; return; }int mid = l + r >> 1;if( pos <= mid ) modify( t[now].l, l, mid, pos, val );else modify( t[now].r, mid + 1, r, pos, val );t[now].val = 1ll * t[t[now].l].val * t[t[now].r].val % mod; }void modify( int lst, int &now, int l, int r, int pos, int val ) {if( ! now ) now = ++ tot;if( l == r ) { t[now].val = val; return; }int mid = l + r >> 1;if( pos <= mid ) {t[now].r = t[lst].r;modify( t[lst].l, t[now].l, l, mid, pos, val );}else {t[now].l = t[lst].l;modify( t[lst].r, t[now].r, mid + 1, r, pos, val );}t[now].val = 1ll * t[t[now].l].val * t[t[now].r].val % mod; }int query( int now, int l, int r, int L, int R ) {if( ! now or r < L or R < l ) return 1;if( L <= l and r <= R ) return t[now].val;int mid = ( l + r ) >> 1;return 1ll * query( t[now].l, l, mid, L, R ) * query( t[now].r, mid + 1, r, L, R ) % mod; }int main() {sieve();scanf( "%d", &n );for( int i = 1;i <= n;i ++ ) {scanf( "%d", &a[i] );for( int j = 1;j <= cnt;j ++ )if( a[i] % prime[j] == 0 )while( a[i] % prime[j] == 0 )st[j][i][0] ++, a[i] /= prime[j];}build();build( rt[0], 1, n );for( int i = 1;i <= n;i ++ ) {if( ! pos[a[i]] ) modify( rt[0], 1, n, i, a[i] );else nxt[pos[a[i]]] = i;pos[a[i]] = i;}for( int i = 1;i <= n;i ++ )if( nxt[i] ) modify( rt[i - 1], rt[i], 1, n, nxt[i], a[nxt[i]] );else rt[i] = rt[i - 1];for( int i = 1;i <= cnt;i ++ ) {mi[i][0] = 1;for( int j = 1;j < 20;j ++ )mi[i][j] = 1ll * mi[i][j - 1] * prime[i] % mod;}scanf( "%d", &Q );int lst = 0, l, r;while( Q -- ) {scanf( "%d %d", &l, &r );l = ( l + lst ) % n + 1;r = ( r + lst ) % n + 1;if( l > r ) swap( l, r );int ans = 1;for( int i = 1;i <= cnt;i ++ )ans = 1ll * ans * mi[i][query( i, l, r )] % mod;ans = 1ll * ans * query( rt[l - 1], 1, n, l, r ) % mod;printf( "%d\n", lst = ans );}return 0; }Colorful Squares
CodeForces-gym102979-C
二分正方形邊長,轉化為判斷性問題
考慮掃描線
對于大小為ddd的正方形,先把xi=xx_i=xxi?=x的點加入,再在x=xi+d+1x=x_i+d+1x=xi?+d+1的時候刪除
最后判斷集合中是否存在一個yyy,滿足[y?d,y][y-d,y][y?d,y]內的不同顏色種類數達到kkk
現在先加一個點[x0,y0][x_0,y_0][x0?,y0?]進集合,那么[y0?d,y][y_0-d,y][y0??d,y]這個區間內就有c0c_0c0?這種顏色
設tit_iti? :y=iy=iy=i時被多少種不同顏色覆蓋
- 加一個點[x0,y0][x_0,y_0][x0?,y0?]對應的就是[y0?d,y][y_0-d,y][y0??d,y]的區間加
- 刪除一個點[x0,y0][x_0,y_0][x0?,y0?]就是區間減
- 判斷是否存在kkk種顏色就是區間max\rm maxmax是否等于kkk
當然可能[y0?d,y][y_0-d,y][y0??d,y]這個區間之前已經被c0c_0c0?顏色覆蓋過一部分,對于之前被覆蓋過的區間就不需要操作
所以區間修改前還要調整計算真正的區間
這個調整不會很復雜
因為沒被覆蓋的區間不會被覆蓋過的區間拆分開,沒被覆蓋的區間一定是完整的一段
#include <set> #include <cstdio> #include <vector> #include <iostream> using namespace std; #define maxn 250005 #define lson num << 1 #define rson num << 1 | 1 struct tree { int Max, tag; }t[maxn << 2]; //線段樹維護區間[l,r]出現不同顏色的種類數 vector < pair < int, int > > pos[maxn]; //橫坐標為x[i]的點集合 multiset < int > col[maxn]; //顏色為c[i]的點的y坐標集合 int n, k;void pushdown( int num ) {t[lson].Max += t[num].tag;t[lson].tag += t[num].tag;t[rson].Max += t[num].tag;t[rson].tag += t[num].tag;t[num].tag = 0; }void build( int num, int l, int r ) {t[num].Max = t[num].tag = 0;if( l == r ) return;int mid = l + r >> 1;build( lson, l, mid );build( rson, mid + 1, r ); }void modify( int num, int l, int r, int L, int R, int x ) {if( R < L or R < l or r < L ) return;if( L <= l and r <= R ) { t[num].Max += x, t[num].tag += x; return; }pushdown( num );int mid = l + r >> 1;modify( lson, l, mid, L, R, x );modify( rson, mid + 1, r, L, R, x );t[num].Max = max( t[lson].Max, t[rson].Max ); }bool check( int d ) {for( int i = 0;i <= n;i ++ ) col[i].clear();build( 1, 1, n );int L, R;for( int i = 1;i <= n;i ++ ) {for( auto now : pos[i] ) {//枚舉橫坐標為i的所有點int y = now.first, c = now.second;/*now點覆蓋的y坐標為[y-d,y]但是這段區間中不能和重復出現過的顏色區間有交e.g. 顏色1 覆蓋了[2,4] 又有顏色1能覆蓋[3,5] 那么只能對[5,5]進行線段樹加上顏色1的操作因為邊長都是d 所以覆蓋區間不可能一長一短導致區間分裂 每個點影響的區間都是一段完整不分裂的區間 */if( col[c].find( y ) != col[c].end() ) {//如果顏色c在縱坐標y的一條橫掃描線上出現過 不再加入 col[c].insert( y );continue;}else col[c].insert( y );//[L,R]表示顏色c的now點真正貢獻的區間 接下來就是求解范圍限制 auto l = col[c].lower_bound( y );//找到顏色c中第一個大于等于y的點位置的縱坐標 if( l == col[c].begin() ) L = max( 1, y - d );//沒有比現在now的縱坐標更小的點了 下限可以取完 防止溢出1 else L = max( *( --l ) + 1, y - d ); //--l第一個小于y的點位置縱坐標+1和覆蓋左端點取max 避免重復 auto r = col[c].upper_bound( y );//找到顏色中第一個大于y的點 if( r == col[c].end() ) R = y;//沒有比現在now點縱坐標更大的點了 else R = min( *r - d - 1, y );//點的覆蓋區間有沒有包含現在的now點 //要保證覆蓋區間是合法的L<=R 這個可以在線段樹最開始的時候判斷 modify( 1, 1, n, L, R, 1 );}if( i - d - 1 > 0 ) {for( auto now : pos[i - d - 1] ) {//刪掉與現在橫坐標距離>d的點 因為是一個一個枚舉 每次只用刪去距離恰好為d+1的點 更小x的點肯定在前面就被其它的i'刪掉了 int y = now.first, c = now.second;col[c].erase( col[c].find( y ) );if( col[c].find( y ) != col[c].end() ) continue;//如果縱坐標y這里還有點 那么此時刪去的點對線段樹內值不會造成影響//否則就同樣的找到顏色c的這個now點真正貢獻的區間auto l = col[c].lower_bound( y );if( l == col[c].begin() ) L = max( 1, y - d );else L = max( *( --l ) + 1, y - d );auto r = col[c].upper_bound( y );if( r == col[c].end() ) R = y;else R = min( *r - d - 1, y );modify( 1, 1, n, L, R, -1 );}}if( t[1].Max == k ) return 1;}return 0; }int main() {scanf( "%d %d", &n, &k );for( int i = 1, x, y, c;i <= n;i ++ ) {scanf( "%d %d %d", &x, &y, &c );pos[x].push_back( make_pair( y, c ) );}n = maxn - 5;int l = 0, r = n, ans;while( l <= r ) {int mid = l + r >> 1;if( check( mid ) ) ans = mid, r = mid - 1;else l = mid + 1;}printf( "%d\n", ans );return 0; }總結
以上是生活随笔為你收集整理的线段树/扫描线问卷调查反馈——Rmq Problem / mex(主席树),Boring Queries(二分+st表+主席树),Colorful Squares(扫描线)的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 王者荣耀嬴政皮肤了解
- 下一篇: 数据结构之trie树——First! G