題解 神奇的集合

2022-02-19 19:11:53 字數 3756 閱讀 9639

不僅讓ssw02考場自閉,而且改的ssw02也差點自閉,改了ssw02乙個停課的下午

ssw02很少寫動態開點的啊

description

毒瘤的線段樹操作

input

從檔案multiset.in 中讀入資料。

第一行兩個正整數n; q, 表示集合個數和詢問數量。

接下來q 行,首先是乙個整數opt:

若opt = 1,接下來三個整數l; r; x,表示向編號[l; r] 的集合中加入x。

若opt = 2,接下來兩個整數l; r,表示詢問編號[l; r] 的集合的元素個數和。

output

輸出到檔案multiset.out 中。

對於每個詢問,輸出一行乙個整數,表示答案。

in.1

4 41 1 2 1

1 1 2 2

1 1 4 1

2 1 4

out.1

10資料範圍與約定

對於20% 資料,n 500;m 500,資料隨機。

對於另10% 資料,所有1 操作的x 均不相同。

對於另20% 資料,所有1 操作的x 均相同。

對於另20% 資料,x 20。

對於另10% 資料,1 操作的l = r。

對於100% 資料,1 n; q 200000,1 opt 2,1 l r n,1 x n。

思考

部分分暴力打好點可以最多拿70(ssw02意思是線段樹+暴力)

如果讓每個點記錄加過的數,那麼空間是肯定不夠的。如果考慮用每乙個加過的數來記錄加過的區間,那麼就有了思路。

我們嘗試開 n 棵樹來記錄每個點覆蓋的區間,當然不能直接建樹,否則會浪費大量空間。

每一次加數操作或者查詢操作都是乙個區間,如果動態開點最多隻會開 2logn 個點 ,空間複雜度不會超過 k nlogn ( k為乙個不大的2位數的常數 )。

處理:

1.每次增加操作時,判斷該區間是否被完全覆蓋,否則接著分。同時還要記錄是否開點(動態開點線段樹基操)。

然後判斷是應當加還是乘(分至乙個完全覆蓋區間)

if( !p )p = ++cnt ;

int mid = ( l+r )>>1 ;

if( l >= x && r <= y )

else if( !t[ p ].w )

else

} pushdown( p , l , r ) ;

if( x <= mid )change_s( t[ p ].ls , l , mid , x , y ) ;

if( y > mid )change_s( t[ p ].rs , mid+1 , r , x , y ) ;

t[ p ].w = t[ t[p].ls ].w + t[ t[p].rs ].w ;

2.開點下傳判定(接上)

void pushdown( int p , int l , int r )

}

3.然後就是區間加,區間乘,區間求和

void mul( int p , int l , int r , int x , int y , int val )

spread( p ) ;

int mid = ( l+r )>>1 ;

if( x <= mid )mul( t[ p ].ls , l , mid , x , y , val ) ;

if( y > mid )mul( t[ p ].rs , mid+1 , r , x , y , val ) ;

//if( wrong == 5 )cout<>1 ;

if( x <= mid )add( t[ p ].ls , l , mid , x , y , val ) ;

if( y > mid )add( t[ p ].rs , mid+1 , r , x , y , val ) ;

t[ p ].sum = t[ t[p].ls ].sum + t[ t[p].rs ].sum ;

}

#includeusing namespace std ;

const int maxn = 200005 ;

const long long mod = 998244353 ;

#define ll long long

inline int read()

struct segt[ maxn*30 ];

int n , q , cnt = 0 , root[ maxn ] , wrong = 0 ;

void build( int &p , int l , int r )

int mid = ( l+r )>>1 ;

build( t[ p ].ls , l , mid ) , build( t[ p ].rs , mid+1 , r ) ;

}void spread( int p )

if( t[ p ].add )

}void mul( int p , int l , int r , int x , int y , int val )

spread( p ) ;

int mid = ( l+r )>>1 ;

if( x <= mid )mul( t[ p ].ls , l , mid , x , y , val ) ;

if( y > mid )mul( t[ p ].rs , mid+1 , r , x , y , val ) ;

//if( wrong == 5 )cout<>1 ;

if( x <= mid )add( t[ p ].ls , l , mid , x , y , val ) ;

if( y > mid )add( t[ p ].rs , mid+1 , r , x , y , val ) ;

t[ p ].sum = t[ t[p].ls ].sum + t[ t[p].rs ].sum ;

}void pushdown( int p , int l , int r )

}void change_s( int &p , int l , int r , int x , int y )

else if( !t[ p ].w )

else

} pushdown( p , l , r ) ;

if( x <= mid )change_s( t[ p ].ls , l , mid , x , y ) ;

if( y > mid )change_s( t[ p ].rs , mid+1 , r , x , y ) ;

t[ p ].w = t[ t[p].ls ].w + t[ t[p].rs ].w ;

}ll ask( int p , int l , int r , int x , int y )

void check( int p , int l , int r )

int main()

else printf("%lld\n",ask( root[ 0 ] , 1 , n , m2 , m3 ) ) ;

} return 0 ;

}

python中神奇的集合

從上節學習過字串後,心中就在思考,想要儲存多個字串怎麼辦?c中有陣列可以實現上述功能,那python呢?現在就讓我們來一 竟吧。python中提供了一種神奇的資料型別list,他有多神奇呢,大家先不要急,讓我婉婉到來,茶要慢慢品嘛。list是一種有序的集合,可以隨時對他進行增減等操作。比如需要列出小...

集合劃分 題解

這道題思路挺清奇的。屬於那種既考演算法模板又考思維的一類題。看到乙個點,可以放在 a 或 b 兩個集合,就要聯想到2 sat問題。這道題如果只讓判斷是否存在合法方案,並輸出任意一種方案,那這題就屬於2 sat問題的模板。但是這題比較難的一點就是求方案數。一般的2 sat問題能不能求方案數呢?我在網上...

集合選數題解

構造神題,對於每乙個不含質因數2和3的數字,我們構造乙個矩陣 可能並不滿 第一行第一列是這個數,在同一行中,下一列的數是這一列的數的2倍,在同一列中,下一行的數是這一行的數的3倍。如果我們選矩陣中乙個數,則右邊的數是這個數的兩倍而不能選,同理,下面的數同樣不能選,不能選左邊的數,因為這個數是它的兩倍...