洛谷P3273 棘手的操作

2021-10-22 14:29:07 字數 3376 閱讀 6575

由於 cnblogs 到 3 月 25 號前需要整改,無法寫部落格,所以這幾天就暫時在 csdn 寫,到時候再搬過去。

nn 個節點,標號從 1

11 到 n

nn,這 n

nn 個節點一開始相互不連通。第 i

ii 個節點的初始權值為 a[i

]a[i]

a[i] n,m

≤3×1

05

n,m\leq 3\times 10^5

n,m≤3×

105。

這道題看著就很左偏樹啊,但是由於操作太多容易寫掛。

發現我們不能直接用線段樹維護的原因是每次的連通塊中的點標號可能並不連續,所以一次操作最壞複雜度是 o(n

log⁡n)

o(n\log n)

o(nlogn)

的。也就是說,如果我們可以調整點的編號,在兩個連通塊合併的時候,使得這兩個連通塊的編號也連續,那麼就可以用線段樹維護了。

如果我們只保留所有連通塊第一次連線時的邊,那麼就會形成一棵樹。每次將兩棵樹的樹根連線起來,然後將每乙個點重新編號為這棵樹的 dfs 序,不難發現這樣就可以滿足每次離岸邊所連線的兩個連通塊的編號是連續的。

然後再列舉一次所有操作用線段樹維護一下就好了。

注意因為加入邊的順序是有要求的,鄰接表會導致加入的邊倒序,所以我採用了 vector 存邊。

時間複雜度 o(m

log⁡n)

o(m\log n)

o(mlogn)

#include

using

namespace std;

const

int n=

300010

,inf=

1e9;

int n,m,tot,x[n]

,y[n]

,a[n]

,id[n]

,siz[n]

,father[n]

,head[n]

;char opt[n][3

];bool vis[n]

;vector<

int> e[n]

;int

find

(int x)

struct segtree

void

pushdown

(int x)

}void

update

(int x,

int l,

int r,

int ql,

int qr,

int k)

pushdown

(x);

int mid=

(l+r)

>>1;

if(ql<=mid)

update

(x*2

,l,mid,ql,qr,k);if

(qr>mid)

update

(x*2+1

,mid+

1,r,ql,qr,k)

;pushup

(x);

}int

query

(int x,

int l,

int r,

int ql,

int qr)

}seg;

void

dfs(

int x)

intmain()

scanf

("%d"

,&m)

;for

(int i=

1;i<=m;i++)}

if(opt[i][0

]=='a'&& opt[i][1

]=='1')

scanf

("%d %d"

,&x[i]

,&y[i]);

if(opt[i][0

]=='a'&& opt[i][1

]=='2')

scanf

("%d %d"

,&x[i]

,&y[i]);

if(opt[i][0

]=='a'&& opt[i][1

]=='3')

scanf

("%d"

,&y[i]);

if(opt[i][0

]=='f'&& opt[i][1

]=='1')

scanf

("%d"

,&x[i]);

if(opt[i][0

]=='f'&& opt[i][1

]=='2')

scanf

("%d"

,&x[i]);

} tot=0;

for(

int i=

1;i<=n;i++)if

(!vis[i]

)dfs

(i);

for(

int i=

1;i<=n;i++

) seg.

update(1

,1,n,id[i]

,id[i]

,a[i]);

for(

int i=

1;i<=n;i++

) father[i]

=i,siz[i]=1

;for

(int i=

1;i<=m;i++)}

if(opt[i][0

]=='a'&& opt[i][1

]=='1')

seg.

update(1

,1,n,id[x[i]

],id[x[i]

],y[i]);

if(opt[i][0

]=='a'&& opt[i][1

]=='2')

if(opt[i][0

]=='a'&& opt[i][1

]=='3')

seg.

update(1

,1,n,1

,n,y[i]);

if(opt[i][0

]=='f'&& opt[i][1

]=='1')

printf

("%d\n"

,seg.

query(1

,1,n,id[x[i]

],id[x[i]])

);if(opt[i][0

]=='f'&& opt[i][1

]=='2')

if(opt[i][0

]=='f'&& opt[i][1

]=='3')

printf

("%d\n"

,seg.

query(1

,1,n,1

,n));}

return0;

}

洛谷P3273 SCOI2011 棘手的操作

有n個節點,標號從1到n,這n個節點一開始相互不連通。第i個節點的初始權值為a i 接下來有如下一些操作 輸入格式 輸入的第一行是乙個整數n,代表節點個數。接下來一行輸入n個整數,a 1 a 2 a n 代表n個節點的初始權值。再下一行輸入乙個整數q,代表接下來的運算元。最後輸入q行,每行的格式如題...

洛谷P3273 SCOI2011 棘手的操作

有n個節點,標號從1到n,這n個節點一開始相互不連通。第i個節點的初始權值為a i 接下來有如下一些操作 u x y 加一條邊,連線第x個節點和第y個節點a1 x v 將第x個節點的權值增加va2 x v 將第x個節點所在的連通塊的所有節點的權值都增加va3 v 將所有節點的權值都增加vf1 x 輸...

洛谷 P1573 棧的操作

題目描述 現在有四個棧,其中前三個為空,第四個棧從棧頂到棧底分別為1,2,3,n。每乙個棧只支援一種操作 彈出並壓入。它指的是把其中乙個棧a的棧頂元素x彈出,並馬上壓入任意乙個棧b中。但是這樣的操作必須符合一定的規則才能進行。規則1 a棧不能為空。規則2 b棧為空或x比b棧棧頂要小。對於給定的n,請...