zoukankan      html  css  js  c++  java
  • kruskal重构树

    不同问题的“最优”有不同含义,有的是“最小”,有的是“最大”。本篇博客“最小”就是“最优”。


    用kruskal求最小生成树时,假设现在要把 ( u , v ) 加入生成树;

    新建一个节点cnt,使 cnt 权值为 dis [ u ] [ v ] ,并连接 ( cnt , u ) , ( cnt , v );

    最后新建点与原有点形成了一个树形结构,这就是kruskal重构树。

    从建树过程我们可以看出,所有原有节点都是叶子节点。

    受加入顺序的限制,对于新建点,后加的点一定比先加的点权值大。

    从重构树的角度来说,对于新建点,其祖先的权值一定比这个点大。

    所以我们说,重构树是满足堆性质的

    不难想到,两个节点的LCA就是最小生成树上两点之间的最长边。

    那么,kruskal重构树可以解决什么问题呢?

    它可以将连通块的问题转化到树上。

    举个例子:

    给出一张无向连通图,边有边权。询问s,k,表示从s出发,不经过边权超过k的边,最多到达多少个点。

    可以将原图重构,在重构树上从s出发,倍增找到权值不超过k的深度最小的祖先fa。

    那么以fa为根的子树的叶子节点都可以经过权值不超过k的边互相到达。

    重构过程实际上是以边权为特征值,将满足特征值的节点加到一个子树内,将连通块的问题转移到了树上。

    这样做有关特征值的查询时,只需找到相应的根节点就可以了。

    归程

    实际上就是,求从v出发,只经过海拔小于p的边所到的点中,到1的最短距离。

    先来一波最短路,然后按海拔重构,这里构造最大生成树,使得根节点得海拔最低。

    然后倍增求祖先的位置。

      1 #include<iostream>
      2 #include<cstdio>
      3 #include<string>
      4 #include<cstring>
      5 #include<queue>
      6 #include<algorithm>
      7 #define mkpr make_pair
      8 #define LL long long
      9 using namespace std;
     10 const int N=200005,M=400005;
     11 inline LL read(){
     12     LL x=0;char ch=getchar();
     13     while(ch<'0'||ch>'9')ch=getchar();
     14     while(ch>='0'&&ch<='9'){
     15         x=x*10+ch-'0';
     16         ch=getchar();
     17     }
     18     return x;
     19 }
     20 
     21 LL T,n,m,cnt,q,k,s;
     22 
     23 struct _mp{
     24     int x,y,h;
     25     LL l;
     26 }mp[M<<1];//原图 
     27 
     28 struct node{
     29     int y,nxt;LL l;
     30 }e[M<<1];//复制一份跑最短路 
     31 int he[M<<1],tot=0;
     32 inline void ad(int x,int y,LL l){
     33     ++tot;
     34     e[tot].y=y;e[tot].l=l;e[tot].nxt=he[x];he[x]=tot;
     35 }
     36 
     37 inline void init(){
     38     n=read();m=read();cnt=n;
     39     for(int i=1;i<=m;++i){
     40         mp[i].x=read();mp[i].y=read();mp[i].l=(LL)read();mp[i].h=read();
     41         ad(mp[i].x,mp[i].y,mp[i].l);ad(mp[i].y,mp[i].x,mp[i].l);
     42     }
     43 }
     44 
     45 LL d[M<<1];
     46 int v[N];
     47 priority_queue<pair<LL,int> > que;
     48 inline void dij(){
     49     memset(v,0,sizeof(v));
     50     d[1]=0;que.push(mkpr(0,1));
     51     while(!que.empty()){
     52         int x=que.top().second;que.pop();
     53         if(v[x])continue;
     54         v[x]=1;
     55         for(int i=he[x];i;i=e[i].nxt){
     56             int y=e[i].y;
     57             if(d[y]>d[x]+e[i].l)
     58                 d[y]=d[x]+e[i].l,que.push(mkpr(-d[y],y));
     59         }
     60     }
     61 }
     62 
     63 LL mn[M<<1];
     64 int fa[M<<1][25];
     65 inline void dfs(int now){
     66     mn[now]=d[now];
     67     for(int i=he[now];i;i=e[i].nxt){
     68         int y=e[i].y;
     69         fa[y][0]=now;
     70         dfs(y);
     71         mn[now]=min(mn[now],mn[y]);
     72     }
     73 }//统计每个子树内的点到1的最短距离 
     74 
     75 int f[M<<1],val[M];
     76 inline bool cmp(_mp a,_mp b){
     77     return a.h>b.h;
     78 }
     79 inline int fnd(int x){
     80     return f[x]==x?x:(f[x]=fnd(f[x]));
     81 }
     82 inline void kru(){
     83     memset(he,0,sizeof(he));tot=0;
     84     sort(mp+1,mp+m+1,cmp);
     85     for(int i=1;i<=n;++i)f[i]=i;
     86     for(int i=1;i<=m;++i){
     87         int fx=fnd(mp[i].x),fy=fnd(mp[i].y);
     88         if(fx==fy)continue;
     89         ++cnt;
     90         f[fx]=f[fy]=f[cnt]=cnt;
     91         val[cnt]=mp[i].h;//新建节点 
     92         ad(cnt,fx,0);ad(cnt,fy,0);
     93     }
     94     dfs(cnt);
     95 }
     96 
     97 inline void clr(){
     98     memset(mn,0x3f,sizeof(mn));
     99     memset(he,0,sizeof(he));tot=0;
    100     memset(f,0,sizeof(f));
    101     memset(d,0x3f,sizeof(d));
    102 }
    103 
    104 int main(){
    105     T=read();
    106     while(T--){
    107         clr();
    108         init();
    109         dij();
    110         kru();
    111         for(int j=1;(1<<j)<=cnt;++j){
    112             for(int i=1;i<=cnt;++i){
    113                 fa[i][j]=fa[fa[i][j-1]][j-1];
    114             }
    115         }
    116         q=read();k=read();s=read();
    117         LL ans=0;
    118         LL h0,s0;
    119         while(q--){
    120             s0=read();h0=read();
    121             h0=(h0+k*ans)%(s+1);
    122             s0=(s0+k*ans-1)%n+1;
    123             for(int j=22;j>=0;--j){
    124                 if(fa[s0][j]&&val[fa[s0][j]]>h0)s0=fa[s0][j];
    125             }
    126             printf("%lld
    ",mn[s0]);
    127             ans=mn[s0];
    128         }
    129     }
    130     return 0; 
    131 }
    132 /*这份代码LL int 混用,空间开的很不严谨。 
    133   让人看着很不爽。
    134   但我不想改了。 
    135 */

    我还想过树剖来着

  • 相关阅读:
    Android 禁用以及捕捉home键
    android中正确导入第三方jar包
    使用SharedPreferences进行数据存储
    tomcat不安全因素
    spring边边角角
    宏定义
    C++变量对比java变量所占内存
    结构指针的分析
    对结构使用指针
    什么是程序文件?
  • 原文地址:https://www.cnblogs.com/chiyo/p/11271857.html
Copyright © 2011-2022 走看看