日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當(dāng)前位置: 首頁(yè) > 编程资源 > 编程问答 >内容正文

编程问答

[Cnoi2019]须臾幻境(LCT维护最大生成树+主席树/分块)

發(fā)布時(shí)間:2023/12/3 编程问答 48 豆豆
生活随笔 收集整理的這篇文章主要介紹了 [Cnoi2019]须臾幻境(LCT维护最大生成树+主席树/分块) 小編覺得挺不錯(cuò)的,現(xiàn)在分享給大家,幫大家做個(gè)參考.

文章目錄

  • title
  • solution
  • code

title

solution

一棵nnn個(gè)點(diǎn)的樹有n?1n-1n?1條邊。

一般的,對(duì)于森林而言則有點(diǎn)數(shù)-邊數(shù)=樹的個(gè)數(shù)

那么我們將無(wú)向圖隨便生成一個(gè)生成樹,形成森林。

詢問的連通塊的個(gè)數(shù)就變?yōu)榍笊值臉涞膫€(gè)數(shù),即森林的點(diǎn)數(shù) n ?-? 森林的邊數(shù) === 連通塊的個(gè)數(shù)

然后就可以用LCTLCTLCT動(dòng)態(tài)維護(hù)生成樹了,將詢問的 rrr 從小到大排序,主席樹維護(hù)每條邊是否在當(dāng)前的生成樹森林中出現(xiàn)。

在第 rrr 個(gè)版本的主席樹中查 [l,r][l,r][l,r] 這段區(qū)間有多條邊存在,這就是生成樹森林的總邊數(shù)。

LCTLCTLCT不好維護(hù)邊權(quán),建虛點(diǎn)連邊即可。

對(duì)于[l,r][l,r][l,r]包含的邊,假設(shè)就用先后輸入的時(shí)間決定先后。

對(duì)于邊iii,加入后若不能形成環(huán),則直接加入;若形成環(huán)了,刪掉環(huán)上最早的邊
這樣越往后的邊越有可能存在于[l,r][l,r][l,r]rrr是遞增的嘛。

所以正確性也是可以保障的。

?:有自環(huán)哦!

code

#include <cstdio> #include <iostream> using namespace std; #define maxn 400005 #define inf 0x7f7f7f7f struct node {int u, v; }edge[maxn];struct LCT {int f, rev, val, minn;int son[2]; }tree[maxn]; int st[maxn];bool notroot( int x ) {return tree[tree[x].f].son[0] == x || tree[tree[x].f].son[1] == x; }void update( int x ) {tree[x].minn = min( tree[x].val, min( tree[tree[x].son[0]].minn, tree[tree[x].son[1]].minn ) ); }void reverse( int x ) {swap( tree[x].son[0], tree[x].son[1] );tree[x].rev ^= 1; } void pushdown( int x ) {if( ! tree[x].rev ) return;reverse( tree[x].son[0] );reverse( tree[x].son[1] );tree[x].rev = 0; }void rotate(int x) { int fa = tree[x].f, Gfa = tree[fa].f;int k = tree[fa].son[1] == x;if( notroot( fa ) )tree[Gfa].son[tree[Gfa].son[1] == fa] = x;tree[x].f = Gfa;tree[fa].son[k] = tree[x].son[k ^ 1];if( tree[x].son[k ^ 1] )tree[tree[x].son[k ^ 1]].f = fa;tree[x].son[k ^ 1] = fa;tree[fa].f = x;update( fa );update( x ); }void splay( int x ) {int top = 0, y = x;st[++ top] = y;while( notroot( y ) ) st[++ top] = y = tree[y].f;while( top ) pushdown( st[top --] );while( notroot( x ) ) {int fa = tree[x].f, Gfa = tree[fa].f;if( notroot( fa ) ) ( ( tree[Gfa].son[0] == fa ) ^ ( tree[fa].son[0] == x ) ) ? rotate( x ) : rotate( fa );rotate( x );}update( x ); }void access( int x ) {for( int son = 0;x;son = x, x = tree[x].f ) {splay( x );tree[x].son[1] = son;update( x );} }void makeroot( int x ) {access( x );splay( x );swap( tree[x].son[0], tree[x].son[1] );tree[x].rev ^= 1; }void split( int x, int y ) {makeroot( x );access( y );splay( y ); }int findroot( int x ) {access( x );splay( x );while( tree[x].son[0] ) x = tree[x].son[0];splay( x );return x; }void link( int x, int y ) {split( x, y );tree[x].f = y; }void cut( int x, int y ) {split( x, y );tree[x].f = tree[y].son[0] = 0; }int ask( int x, int y ) {split( x, y );return tree[y].minn; }struct SegmentTree {int cnt, l, r; }seg[maxn * 20]; int tot; int rt[maxn];void modify( int &num, int l, int r, int pos, int v ) {int x = ++ tot;seg[x].l = seg[num].l, seg[x].r = seg[num].r, seg[x].cnt = seg[num].cnt + v, num = x;if( l == r ) return;int mid = ( l + r ) >> 1;if( pos <= mid ) modify( seg[num].l, l, mid, pos, v );else modify( seg[num].r, mid + 1, r, pos, v ); }int query( int num, int l, int r, int L, int R ) {if( L <= l && r <= R ) return seg[num].cnt;int mid = ( l + r ) >> 1, ans = 0;if( L <= mid ) ans += query( seg[num].l, l, mid, L, R );if( mid < R ) ans += query( seg[num].r, mid + 1, r, L, R );return ans; }int n, m, Q, t, last;void get( int &l, int &r ) {if( t ) l = ( l + last ) % m + 1, r = ( r + last ) % m + 1; if( l > r ) swap( l, r ); }int main() {scanf( "%d %d %d %d", &n, &m, &Q, &t );for( int i = 0;i <= n;i ++ ) tree[i].val = tree[i].minn = inf;for( int i = 1;i <= m;i ++ ) {scanf( "%d %d", &edge[i].u, &edge[i].v );tree[i + n].val = tree[i + n].minn = i;}for( int i = 1;i <= m;i ++ ) {int u = edge[i].u, v = edge[i].v;rt[i] = rt[i - 1];if( u == v ) continue;modify( rt[i], 1, m, i, 1 );if( findroot( u ) == findroot( v ) ) {int pos = ask( u, v );cut( edge[pos].u, pos + n );cut( edge[pos].v, pos + n );modify( rt[i], 1, m, pos, -1 );}link( u, i + n );link( v, i + n );}while( Q -- ) {int l, r;scanf( "%d %d", &l, &r );get( l, r );last = n - query( rt[r], 1, m, l, r );printf( "%d\n", last );}return 0; }

總結(jié)

以上是生活随笔為你收集整理的[Cnoi2019]须臾幻境(LCT维护最大生成树+主席树/分块)的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網(wǎng)站內(nèi)容還不錯(cuò),歡迎將生活随笔推薦給好友。