CF464E The Classic Problem(主席树+哈希+最短路)
CF464E The Classic Problem
- problem
- solution
- code
problem
題目鏈接
solution
經(jīng)典題。
本題很特別的是每條邊的邊權(quán)都是 222 的冪,而且數(shù)據(jù)讀入的是指數(shù)。
所以可以將路徑權(quán)值看作一個二進(jìn)制串,每加一條邊就是對應(yīng)二進(jìn)制位 +1+1+1,當(dāng)然會有相應(yīng)的進(jìn)位操作。
用一棵線段樹維護(hù)一個二進(jìn)制串。
考慮加邊的操作。
-
如果該位原先是 000 ,那么直接 +1+1+1 ,即線段樹的單點(diǎn)修改。
-
如果該位原先是 111,那么就會導(dǎo)致進(jìn)位問題,需要從當(dāng)前位置開始找一段連續(xù)最長都為 111 的串,在第一個 000 的位置停下。然后將最終位置 +1+1+1,中間連續(xù)一段全都清零。即線段樹的區(qū)間修改和單點(diǎn)修改。
從當(dāng)前位置開始找一段連續(xù)的串,可以二分長度然后判斷,但是這樣線段樹一次操作就是 O(log?2n)O(\log^2n)O(log2n) 的,還有最短路的部分呢?!
所以我們在線段樹上二分實(shí)現(xiàn),降為 O(log?n)O(\log n)O(logn)。
最短路就是堆優(yōu)化的 djikstra\text{djikstra}djikstra。
只不過比較兩個字符串的大小,需要重載排序規(guī)則,所以實(shí)現(xiàn)是手打堆。
比較字符串大小肯定是哈希,所以線段樹還要順便維護(hù)一下串的哈希值,從高位開始比較,時間是 O(log?n)O(\log n)O(logn) 的。
最后是最短路的收縮,一個點(diǎn)可能更新多個點(diǎn)的最短路,但是多個點(diǎn)之間是獨(dú)立的。
所以最短路徑串不能改在原來的上面,每次修改都是產(chǎn)生一個新串。
因此我們采用主席樹,動態(tài)開點(diǎn)。
總時間復(fù)雜度 :O(nlog?2n):O(n\log^2n):O(nlog2n)。
部分具體實(shí)現(xià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)位 開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)制級別高于lson 因?yàn)槎褍?yōu)化的dijkstra u點(diǎn)可能會導(dǎo)致若干個v點(diǎn)的最短路 而每一個v之間是獨(dú)立的 所以得用主席樹 一棵線段樹其實(shí)本質(zhì)是一個二進(jìn)制串 主席樹相當(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的個數(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 ) {//初始化串上的每個位置都是數(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開始的最長連續(xù)的1 即從pos開始的第一個0位置 對應(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é)果 對應(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](最開始的線段樹)全是0 所以在修改區(qū)間內(nèi)就直接指向root[0] 指針即可//沒必要做無謂的動態(tà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 對應(yīng)操作為在第w位加1int pos = query( rt, w ); //找到可能觸發(fā)進(jìn)位后的第一個為0位置int now = modify( rt, pos );if( pos == w ) return now; //沒有觸發(fā)進(jìn)位 就在原本的位置+1 就不會有后面的清除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這棵樹代表的二進(jìn)制串和y代表的二進(jìn)制串的大小 優(yōu)先從高位(右兒子)開始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比較很容易沖撞 再來一個自然溢出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ān)持創(chuàng)作打卡瓜分現(xiàn)金大獎總結(jié)
以上是生活随笔為你收集整理的CF464E The Classic Problem(主席树+哈希+最短路)的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 7000 MB/s读速 + 2G 独立缓
- 下一篇: 余承东:问界 M9 盲订已超 3 万台,