kruskal重構樹學習筆記

  kruskal重構樹,其實本質上就是個可持久化的kruskal。算法

  kruskal算法是用來求解最小生成樹的,而最小生成樹有另一個性質:它也是最小瓶頸樹,即圖上兩點之間通過邊權最大的邊最小的路徑,都是生成樹上兩點間的路徑。咱們利用這一性質,能夠在kruskal算法的求解過程當中處理一些東西,例如維護圖上只保留邊權小於某個值的邊時,圖的連通性。ide

  可是,若是這一維護過程要求強制在線,那麼就必須用到kruskal重構樹了。url

  kruskal重構樹是一棵有根二叉樹,其實就是把kruskal的過程用一棵二叉樹記錄下來。每次用一條邊合併兩個連通點集時,新建一個節點,點權爲加入邊的邊權,它的兩個兒子分別爲表示兩個連通點集的節點。spa

  根據kruskal算法,咱們能夠獲得kruskal重構樹的一些重要性質:.net

    1. 若是忽略葉結點,kruskal重構樹知足堆性質。code

    2. kruskal的每一個子樹是原圖上保留邊權不大於根節點權值的邊後的極大連通子圖。blog

  根據kruskal算法咱們能夠很輕鬆的證實這些性質。get

例題:string

  一、uoj393【NOI2018】歸程it

    這道題須要在線維護無向圖上從某個起點出發,通過邊權不小於某個權值的邊到達的點權最小的點。顯然,從該點開始通過邊權不超過給定權值的邊所能到達的全部點,就是kruskal重構樹上該起點的,點權不小於該權值的最遠祖先的子樹。因此咱們把kruskal重構樹建出來,而後對於每一個節點倍增找祖先,而後直接查詢子樹內的最小點權便可。

    由於實在找不到哪裏爆int,因此就只能暴力#define int long long...

#include<cstdio> #include<cstdlib> #include<cstring> #include<cmath> #include<ctime> #include<algorithm> #include<queue>
#define ll long long
#define int long long
#define inf 1ll<<60
#define maxn 200010 inline ll read() { ll x=0; char c=getchar(),f=1; for(;c<'0'||'9'<c;c=getchar())if(c=='-')f=-1; for(;'0'<=c&&c<='9';c=getchar())x=x*10+c-'0'; return x*f; } inline void write(ll x) { static char buf[20],len; len=0; if(x<0)putchar('-'),x=-x; for(;x;x/=10)buf[len++]=x%10+'0'; if(!len)putchar('0'); else while(len)putchar(buf[--len]); } inline void writesp(ll x){write(x); putchar(' ');} inline void writeln(ll x){write(x); putchar('\n');} struct Edge{ int x,y,h,d; }E[2*maxn]; struct edge{ int to,nxt,d; }e[4*maxn]; struct Data{ int id,x; friend bool operator < (Data a,Data b){return a.x>b.x;} }; std::priority_queue<Data>q; int fir[2*maxn],top[2*maxn],val[2*maxn],fa[2*maxn][20]; int mark[maxn],dist[2*maxn]; int n,m,Q,K,S,cnt,tot; bool cmp(Edge a,Edge b){return a.h>b.h;} int find(int x){return x==top[x]?x:top[x]=find(top[x]);} void add_edge(int x,int y,int d){e[tot].to=y; e[tot].d=d; e[tot].nxt=fir[x]; fir[x]=tot++;} void dijkstra(int S) { for(int i=1;i<=n;i++){ mark[i]=0; dist[i]=inf; } dist[S]=0; Data init={S,0}; q.push(init); while(!q.empty()){ Data now=q.top(); q.pop(); if(mark[now.id])continue; mark[now.id]=1; for(int i=fir[now.id];~i;i=e[i].nxt) if(dist[now.id]+e[i].d<dist[e[i].to]){ dist[e[i].to]=dist[now.id]+e[i].d; Data tmp={e[i].to,dist[e[i].to]}; q.push(tmp); } } } void dfs(int now) { for(int i=1;i<=18;i++) fa[now][i]=fa[fa[now][i-1]][i-1]; if(now>n)dist[now]=inf; for(int i=fir[now];~i;i=e[i].nxt){ fa[e[i].to][0]=now; dfs(e[i].to); dist[now]=std::min(dist[now],dist[e[i].to]); } } void work() { n=read(); m=read(); memset(fir,255,sizeof(fir)); tot=0; for(int i=1;i<=m;i++){ E[i].x=read(); E[i].y=read(); E[i].d=read(); E[i].h=read(); add_edge(E[i].x,E[i].y,E[i].d); add_edge(E[i].y,E[i].x,E[i].d); } dijkstra(1); std::sort(E+1,E+m+1,cmp); for(int i=1;i<=2*n;i++)top[i]=i; memset(fir,255,sizeof(fir)); tot=0; cnt=n; for(int i=1;i<=m;i++){ int fx=find(E[i].x),fy=find(E[i].y); if(fx!=fy){ val[++cnt]=E[i].h; add_edge(cnt,fx,0); add_edge(cnt,fy,0); top[fx]=top[fy]=cnt; } } fa[cnt][0]=0; dfs(cnt); Q=read(); K=read(); S=read(); int lastans=0; while(Q--){ int v=read(),p=read(); v=(v+K*lastans-1)%n+1; p=(p+K*lastans)%(S+1); // printf("%d %d *************\n",v,p);
        int now=v; for(int i=18;i>=0;i--) if(fa[now][i]&&val[fa[now][i]]>p)now=fa[now][i]; lastans=dist[now]; writeln(lastans); } // for(int i=1;i<=cnt;i++) // printf("%d %d %d %d\n",i,dist[i],fa[i][0],val[i]);
} signed main() { int T=read(); while(T--)work(); return 0; }
uoj393

  二、uoj407【IOI2018】狼人

    這道題有兩個偏序條件,咱們先按照偏序條件定義邊權,而後每一個詢問顯然就是尋找有沒有一個點,能同時被起點和終點通過知足各自偏序條件的邊同時到達。因而依舊上kruskal重構樹,不過由於這裏有邊權有兩個偏序條件,所以須要兩維,而後就變成了一個矩形數點的問題,能夠用掃描線+線段樹解決。

    代碼:

#include "werewolf.h" #include<cstdio> #include<cstdlib> #include<cstring> #include<cmath> #include<algorithm> #include<vector>
#define maxn 200010
struct edge{ int from,to,nxt,val; }e[2*maxn]; struct Graph{ edge e[2*maxn]; int fir[2*maxn]; int cnt,tot; inline void reset(int n){memset(fir,255,sizeof(fir)); cnt=n; tot=0;} inline void add_edge(int x,int y){e[tot].from=x; e[tot].to=y; e[tot].nxt=fir[x]; fir[x]=tot++;} }T1,T2; bool cmp1(edge a,edge b){return a.val>b.val;} bool cmp2(edge a,edge b){return a.val<b.val;} int Fa[2*maxn][20]; int fa[2*maxn],val[2*maxn],id[2*maxn],size[2*maxn]; int tot; int find(int x){return fa[x]==x?x:fa[x]=find(fa[x]);} void dfs1(int now) { for(int i=1;i<=18;i++) Fa[now][i]=Fa[Fa[now][i-1]][i-1]; id[now]=++tot; size[now]=1; for(int i=T1.fir[now];~i;i=T1.e[i].nxt){ Fa[T1.e[i].to][0]=now; dfs1(T1.e[i].to); size[now]+=size[T1.e[i].to]; } } void dfs2(int now) { // printf("%d %d\n",now,tot+1);
    for(int i=1;i<=18;i++) Fa[now][i]=Fa[Fa[now][i-1]][i-1]; id[now]=++tot; size[now]=1; for(int i=T2.fir[now];~i;i=T2.e[i].nxt){ Fa[T2.e[i].to][0]=now; dfs2(T2.e[i].to); size[now]+=size[T2.e[i].to]; } } struct point{ int x,y; }a[maxn]; struct rect{ int u,d,l,r,id; }b[maxn]; bool cmp3(point a,point b){return a.x<b.x;} bool cmp4(rect a,rect b){return a.d<b.d;} int sgt[8*maxn]; void modify(int now,int l,int r,int x,int k) { // printf("%d %d %d %d %d @@@\n",now,l,r,x,k);
    if(l==r)sgt[now]=k; else{ int mid=(l+r)>>1; if(x<=mid)modify(now<<1,l,mid,x,k); else modify(now<<1|1,mid+1,r,x,k); sgt[now]=std::max(sgt[now<<1],sgt[now<<1|1]); } } int query(int now,int l,int r,int x,int y) { // printf("%d %d %d %d %d &&&\n",now,l,r,x,y);
    if(x<=l&&r<=y)return sgt[now]; else{ int mid=(l+r)>>1,ans=0; if(x<=mid)ans=std::max(ans,query(now<<1,l,mid,x,y)); if(mid<y)ans=std::max(ans,query(now<<1|1,mid+1,r,x,y)); return ans; } } int n,m,q; std::vector<int> check_validity(int N,std::vector<int> X,std::vector<int> Y,std::vector<int> S,std::vector<int> E,std::vector<int> L,std::vector<int> R) { n=N; m=X.size(); q=S.size(); for(int i=0;i<m;i++){ e[i].from=X[i]+1; e[i].to=Y[i]+1; e[i].val=std::min(X[i],Y[i]); } std::sort(e,e+m,cmp1); // for(int i=0;i<m;i++) // printf("%d %d %d\n",e[i].from,e[i].to,e[i].val);
    for(int i=1;i<=2*n-1;i++)fa[i]=i; T1.reset(n); for(int i=0;i<m;i++){ int fx=find(e[i].from),fy=find(e[i].to); if(fx!=fy){ val[++T1.cnt]=e[i].val; // printf("%d %d &&&\n",T1.cnt,val[T1.cnt]); // printf("%d -> %d\n%d -> %d\n",T1.cnt,fx,T1.cnt,fy);
 T1.add_edge(T1.cnt,fx); T1.add_edge(T1.cnt,fy); fa[fx]=fa[fy]=T1.cnt; } } tot=0; Fa[T1.cnt][0]=0; dfs1(T1.cnt); for(int i=1;i<=n;i++) a[i].x=id[i]; for(int i=0;i<q;i++){ int now=S[i]+1; for(int j=18;j>=0;j--) if(Fa[now][j]&&val[Fa[now][j]]>=L[i])now=Fa[now][j]; b[i].u=id[now]; b[i].d=id[now]+size[now]-1; } for(int i=0;i<m;i++){ e[i].from=X[i]+1; e[i].to=Y[i]+1; e[i].val=std::max(X[i],Y[i]); } std::sort(e,e+m,cmp2); // for(int i=0;i<m;i++) // printf("%d %d %d\n",e[i].from,e[i].to,e[i].val);
    for(int i=1;i<=2*n-1;i++)fa[i]=i; T2.reset(n); for(int i=0;i<m;i++){ int fx=find(e[i].from),fy=find(e[i].to); if(fx!=fy){ val[++T2.cnt]=e[i].val; // printf("%d %d &&&\n",T2.cnt,val[T2.cnt]); // printf("%d -> %d\n%d -> %d\n",T2.cnt,fx,T2.cnt,fy);
 T2.add_edge(T2.cnt,fx); T2.add_edge(T2.cnt,fy); fa[fx]=fa[fy]=T2.cnt; } } tot=0; Fa[T2.cnt][0]=0; // puts("cxktxdy");
 dfs2(T2.cnt); for(int i=1;i<=n;i++) a[i].y=id[i]; for(int i=0;i<q;i++){ int now=E[i]+1; for(int j=18;j>=0;j--) if(Fa[now][j]&&val[Fa[now][j]]<=R[i])now=Fa[now][j]; b[i].l=id[now]; b[i].r=id[now]+size[now]-1; } for(int i=0;i<q;i++)b[i].id=i; // for(int i=1;i<=n;i++) // printf("%d %d %d ***\n",i,a[i].x,a[i].y); // for(int i=0;i<q;i++) // printf("%d %d %d %d %d ***\n",i,b[i].u,b[i].d,b[i].l,b[i].r); // puts("cxktxdy");
    std::sort(a+1,a+n+1,cmp3); std::sort(b,b+q,cmp4); std::vector<int> ans; ans.resize(q); int last=1; for(int i=0;i<q;i++){ // printf("%d ************************\n",i);
        while(last<=n&&a[last].x<=b[i].d){ modify(1,1,2*n-1,a[last].y,a[last].x); ++last; } // puts("cxktxdy");
        ans[b[i].id]=(query(1,1,2*n-1,b[i].l,b[i].r)>=b[i].u); // puts("cxktql");
 } // puts("QAQ");
    return ans; }
uoj407
相關文章
相關標籤/搜索