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

歡迎訪問(wèn) 生活随笔!

生活随笔

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

编程问答

CF464E The Classic Problem(主席树+哈希+最短路)

發(fā)布時(shí)間:2023/12/3 编程问答 47 豆豆
生活随笔 收集整理的這篇文章主要介紹了 CF464E The Classic Problem(主席树+哈希+最短路) 小編覺(jué)得挺不錯(cuò)的,現(xiàn)在分享給大家,幫大家做個(gè)參考.

CF464E The Classic Problem

  • problem
  • solution
  • code

problem

題目鏈接

solution

經(jīng)典題。

本題很特別的是每條邊的邊權(quán)都是 222 的冪,而且數(shù)據(jù)讀入的是指數(shù)。

所以可以將路徑權(quán)值看作一個(gè)二進(jìn)制串,每加一條邊就是對(duì)應(yīng)二進(jìn)制位 +1+1+1,當(dāng)然會(huì)有相應(yīng)的進(jìn)位操作。

用一棵線段樹(shù)維護(hù)一個(gè)二進(jìn)制串。

考慮加邊的操作。

  • 如果該位原先是 000 ,那么直接 +1+1+1 ,即線段樹(shù)的單點(diǎn)修改。

  • 如果該位原先是 111,那么就會(huì)導(dǎo)致進(jìn)位問(wèn)題,需要從當(dāng)前位置開(kāi)始找一段連續(xù)最長(zhǎng)都為 111 的串,在第一個(gè) 000 的位置停下。然后將最終位置 +1+1+1,中間連續(xù)一段全都清零。即線段樹(shù)的區(qū)間修改和單點(diǎn)修改。

    從當(dāng)前位置開(kāi)始找一段連續(xù)的串,可以二分長(zhǎng)度然后判斷,但是這樣線段樹(shù)一次操作就是 O(log?2n)O(\log^2n)O(log2n) 的,還有最短路的部分呢?!

    所以我們?cè)诰€段樹(shù)上二分實(shí)現(xiàn),降為 O(log?n)O(\log n)O(logn)

最短路就是堆優(yōu)化的 djikstra\text{djikstra}djikstra

只不過(guò)比較兩個(gè)字符串的大小,需要重載排序規(guī)則,所以實(shí)現(xiàn)是手打堆。

比較字符串大小肯定是哈希,所以線段樹(shù)還要順便維護(hù)一下串的哈希值,從高位開(kāi)始比較,時(shí)間是 O(log?n)O(\log n)O(logn) 的。

最后是最短路的收縮,一個(gè)點(diǎn)可能更新多個(gè)點(diǎn)的最短路,但是多個(gè)點(diǎn)之間是獨(dú)立的。

所以最短路徑串不能改在原來(lái)的上面,每次修改都是產(chǎn)生一個(gè)新串。

因此我們采用主席樹(shù),動(dòng)態(tài)開(kāi)點(diǎn)。

總時(shí)間復(fù)雜度 :O(nlog?2n):O(n\log^2n):O(nlog2n)

部分具體實(shí)現(xiàn)含義可見(jiàn)代碼注釋。

code

#include <bits/stdc++.h> using namespace std; #define ull unsigned long long //自然溢出 #define mod 1000000007 #define maxn 400005 #define MAX 100040 //2^1e5疊加還要進(jìn)位 開(kāi)1e5+5不夠 struct node { int to, nxt, w; }E[maxn]; int S, T, n, m, cnt = 1; ull base2[maxn]; int head[maxn], lst[maxn], root[maxn], base1[maxn]; bool vis[maxn]; stack < int > ans;void addedge( int u, int v, int w ) {E[cnt] = { v, head[u], w };head[u] = cnt ++; }namespace sgt { /* [0,X]就表示[0,X]的二進(jìn)制位 進(jìn)位是往右邊進(jìn)的 所以rson二進(jìn)制級(jí)別高于lson 因?yàn)槎褍?yōu)化的dijkstra u點(diǎn)可能會(huì)導(dǎo)致若干個(gè)v點(diǎn)的最短路 而每一個(gè)v之間是獨(dú)立的 所以得用主席樹(shù) 一棵線段樹(shù)其實(shí)本質(zhì)是一個(gè)二進(jìn)制串 主席樹(shù)相當(dāng)于是在改串 */#define mid ( ( l + r ) >> 1 )#define maxm maxn * 20int cnt;int hash1[maxm], sum[maxm], lson[maxm], rson[maxm]; //sum[now]:區(qū)間中二進(jìn)制位為1的個(gè)數(shù)ull hash2[maxm];void pushup( int now ) {sum[now] = sum[lson[now]] + sum[rson[now]];hash1[now] = ( hash1[lson[now]] + hash1[rson[now]] ) % mod;hash2[now] = hash2[lson[now]] + hash2[rson[now]];}int build( int x, int l = 0, int r = MAX ) {//初始化串上的每個(gè)位置都是數(shù)字xint now = ++ cnt;if( l == r ) { sum[now] = x;hash1[now] = base1[l] * x;hash2[now] = base2[l] * x;return now;}lson[now] = build( x, l, mid );rson[now] = build( x, mid + 1, r );pushup( now );return now;}int query( int now, int l, int r, int L, int R ) {if( R < l or r < L ) return 0;if( L <= l and r <= R ) return sum[now];return query( lson[now], l, mid, L, R ) + query( rson[now], mid + 1, r, L, R );}int query( int now, int pos, int l = 0, int r = MAX ) {//在pos位+1 查詢從pos開(kāi)始的最長(zhǎng)連續(xù)的1 即從pos開(kāi)始的第一個(gè)0位置 對(duì)應(yīng)的是區(qū)間查詢if( l == r ) return l;if( pos > mid ) return query( rson[now], pos, mid + 1, r ); if( query( lson[now], l, mid, pos, mid ) == mid - pos + 1 ) return query( rson[now], mid + 1, mid + 1, r );//打成了 query(rson[now],pos,mid+1,r) 調(diào)半天哭了elsereturn query( lson[now], pos, l, mid );}int modify( int lst, int pos, int l = 0, int r = MAX ) {//進(jìn)位 前面的若干連續(xù)1導(dǎo)致了向pos進(jìn)位(pos位一定為0)的結(jié)果 對(duì)應(yīng)的是單點(diǎn)修改int now = ++ cnt;lson[now] = lson[lst], rson[now] = rson[lst];if( l == r ) { sum[now] = 1;hash1[now] = base1[l];hash2[now] = base2[l];return now;}if( pos <= mid ) lson[now] = modify( lson[lst], pos, l, mid );else rson[now] = modify( rson[lst], pos, mid + 1, r );pushup( now );return now;}int modify( int x, int y, int L, int R, int l, int r ) {//巧妙運(yùn)用root[0](最開(kāi)始的線段樹(shù))全是0 所以在修改區(qū)間內(nèi)就直接指向root[0] 指針即可//沒(méi)必要做無(wú)謂的動(dòng)態(tài)開(kāi)點(diǎn) 浪費(fèi)空間if( R < l or r < L ) return x;if( L <= l and r <= R ) return y;int now = ++ cnt;lson[now] = modify( lson[x], lson[y], L, R, l, mid );rson[now] = modify( rson[x], rson[y], L, R, mid + 1, r );pushup( now );return now;}int add( int rt, int w ) { //這條邊權(quán)值為2^w 對(duì)應(yīng)操作為在第w位加1int pos = query( rt, w ); //找到可能觸發(fā)進(jìn)位后的第一個(gè)為0位置int now = modify( rt, pos );if( pos == w ) return now; //沒(méi)有觸發(fā)進(jìn)位 就在原本的位置+1 就不會(huì)有后面的清除else return modify( now, root[0], w, pos - 1, 0, MAX ); //一旦進(jìn)位到pos意味著[w,pos)全都是1 需要將這一段清0}bool same( int x, int y ) { return sum[x] == sum[y] and hash1[x] == hash1[y] and hash2[x] == hash2[y];}bool compare( int x, int y, int l = 0, int r = MAX ) { //比較x這棵樹(shù)代表的二進(jìn)制串和y代表的二進(jìn)制串的大小 優(yōu)先從高位(右兒子)開(kāi)始i比較//1代表x<=y 0代表x>yif( l == r ) return sum[x] <= sum[y];if( same( rson[x], rson[y] ) )return compare( lson[x], lson[y], l, mid );elsereturn compare( rson[x], rson[y], mid + 1, r );}}struct heap { int siz, cnt, root;int id[maxn], rt[maxn], dis[maxn], lson[maxn], rson[maxn];int merge( int x, int y ) {if( ! x or ! y ) return x + y;if( sgt :: compare( rt[y], rt[x] ) ) swap( x, y );rson[x] = merge( rson[x], y );if( dis[rson[x]] > dis[lson[x]] ) swap( lson[x], rson[x] ); //啟發(fā)式合并dis[x] = dis[rson[x]] + 1;return x;} void push( int x, int t ) { siz ++, cnt ++;id[cnt] = x;rt[cnt] = t;root = merge( root, cnt ); }void pop() { siz --; root = merge( lson[root], rson[root] ); }int top() { return id[root]; }bool empty() { return siz == 0; }}q;void dijkstra() {int ori = sgt :: build( 1 );for( int i = 1;i <= n;i ++ ) root[i] = ori;root[0] = root[S] = sgt :: build( 0 );q.push( S, root[S] );while( ! q.empty() ) {int u = q.top(); q.pop();if( vis[u] ) continue;vis[u] = 1;for( int i = head[u];i;i = E[i].nxt ) {int v = E[i].to, w = E[i].w;if( vis[v] ) continue;int New = sgt :: add( root[u], w );if( sgt :: compare( root[v], New ) ) continue;root[v] = New, lst[v] = u;q.push( v, root[v] );}}if( root[T] == ori ) { printf( "-1\n" ); return; }printf( "%d\n", sgt :: hash1[root[T]] ); //hash1即為最后結(jié)果 順便可以起hash作用for( int i = T;i ^ lst[S];i = lst[i] ) ans.push( i );printf( "%d\n", (int)ans.size() );while( ! ans.empty() ) printf( "%d ", ans.top() ), ans.pop(); }int main() {base1[0] = base2[0] = 1;for( int i = 1;i <= MAX;i ++ ) //單純2^i最后答案hash比較很容易沖撞 再來(lái)一個(gè)自然溢出hash加強(qiáng)base1[i] = base1[i - 1] * 2 % mod, base2[i] = base2[i - 1] * 17;scanf( "%d %d", &n, &m );for( int i = 1, u, v, w;i <= m;i ++ ) {scanf( "%d %d %d", &u, &v, &w );addedge( u, v, w );addedge( v, u, w );}scanf( "%d %d", &S, &T );dijkstra();return 0; } 創(chuàng)作挑戰(zhàn)賽新人創(chuàng)作獎(jiǎng)勵(lì)來(lái)咯,堅(jiān)持創(chuàng)作打卡瓜分現(xiàn)金大獎(jiǎng)

總結(jié)

以上是生活随笔為你收集整理的CF464E The Classic Problem(主席树+哈希+最短路)的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。

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