笔记-Kruskal重构树(二)

U13笔记

例1:KK3177

题面

题目描述

有一棵 \(n\) 个节点的树,每条边都有一个正整数权值,\(q\) 个问题,询问从 \(v\) 号节点出发,只通过权值不少于 \(k\) 的边,最多能到达多少个除自己之外的节点。

输入格式 recommendation.in

输入第一行包含正整数 \(n\)\(q\)

接着 \(n-1\) 行,每行三个正整数 \(u,v,w\) 代表有一条端点为 \(u\)\(v\) 的权值为 \(w\) 的边。

接着 \(q\) 行,每行两个正整数 \(k\)\(v\)

输出格式

对于每个问询输出一行,每行一个正整数。

输入样例

4 3

1 2 3

2 3 2

2 4 4

1 2

4 1

3 1

输出样例

数据范围

\(n,q \le 100000\)

\(w,k \le 1000000000\)

算法分析

多个离线问询

树上 \(u\) 号节点出发通过边权不小于 \(k\) 的边能够连通到几个其他点

对所有边:按照边权从大到小排序

对所有问询:按照边权参数从大到小排序

双游标:一个游标扫描所有边,一个游标扫描所有问题

主导游标:扫描问题

辅助游标:扫描边

Code

int main(){
    int n,nQ;
    cin>>n>>nQ;
    int nE=n-1;
    for(int iE=1;iE<=n-1;iE++)
        cin>>e[iE].u>>e[iE].v>>e[iE].w;
    sort(e+1,e+1+nE,cmpE);
    for(int iQ=1;iQ<=nQ;iQ++){
        cin>>q[iQ].k>>q[iQ].v;
        q[iQ].id=iQ;
    }
    sort(q+1,q+1+nQ,cmpQ);
    
    for(int u=1;u<=n;u++){
        fa[u]=u;
        sz[u]=1;
        //sz[u] 表示 u 号节点所在的连通块的节点总数
    }
    
    int iE=1;
    for(int iQ=1;iQ<=nQ;iQ++){
        for(;iE<=nE;iE++){
            if(e[iE].w<q[iQ].k)break;
            int ru=root(e[iE].u);
            int rv=root(e[iE].v);
            if(ru==rv)continue;
            fa[ru]=rv;
            sz[rv]+=sz[ru];
        }
        ans[q[iQ].id]=sz[root(q[iQ].v)]-1;
    }
}

加强版题目

如果题目要求强制在线:

暴力

可撤销并查集

算法分析

依然可以预处理完成准备工作

Kruskal 重构树 梳理出边权的大小等级

先建重构树,回答问题时从出发点往上跳到一个最高的合法的祖先,答案就是这个祖先的子树里面的叶子个数-1

Code

void dfs(int u,int fa){
    d[u]=d[fa]+1;
    p[u][0]=fa;
    for(int i=1;i<=L;i++)
        p[u][i]=p[p[u][i-1]][i-1];
	for(int i=0;i<(int)son[u].size();i++)
        dfs(son[u][i],u);
}

//倍增找最高合法祖先
int query(int k,int v){
    if(val[p[v][0]]<k)return 0;
    v=p[v][0];
    for(int i=L;i>=0;i--){
        if(d[v]<=(1<<i))continue;
        if(val[p[v][i]]<k)continue;
        v=p[v][i];
    }
    return nLeafs[v]-1;
}

int main(){
    cin>>n>>nQ;
    int nE=n-1;
    for(int iE=1;iE<=nE;iE++)
        cin>>e[iE].u>>e[iE].v>>e[iE].w;
    for(int u=1;u<=n*2;u++)fa[u]=u;
    for(int u=1;u<=n;u++)nLeafs[u]=1;
    //nLeafs[u] 表示 u 号节点所在子树中有几个叶节点
    int nV=n;
    sort(e+1,e+1+nE,cmpE);
    
    for(int iE=1;iE<=nE;iE++){
        int ru=root(e[iE].u);
        int rv=root(e[iE].v);
        if(ru==rv)continue;
        nV++;
        fa[ru]=fa[rv]=nV;
        nLeafs[nV]=nLeafs[ru]+nLeafs[rv];
        val[nV]=e[iE].w;
        son[nV].push_back(ru);
        son[nV].push_back(rv);
    }
    
    L=log(n)/log(2)+1;
    dfs(nV,0);
    int k,v;
    for(int iQ=1;iQ<=nQ;iQ++){
        cin>>k>>v;
        cout<<query(k,v)<<endl;
    }
}

例2:KK3178(NOI2018归程)

算法分析

预计算:最短路标记

Dijkstra

dst[u] 表示 1 号节点到 \(u\) 号节点的最短距离

重构 MST:梳理海拔层次

Kruskal

minD[u] 表示 \(u\) 号子树内叶节点(原始节点)的最短路标记的最小值

易错点:

多测清空

三张图并存:

  1. 原始海拔高度图 边集数组
  2. 原始距离图 链式前向星
  3. Kruskal重构树 儿子列表

Code

int query(int k,int v){
    if(height[p[v][0]]<=k)return minD[v];
    v=p[v][0];
    for(int i=L;i>=0;i--){
        if(d[v]<=(1<<i))continue;
        
    }
}

void solve(){
    scanf("%d %d",&n,&m);
    nE=0;
    for(int u=1;u<=n;u++)hd[u]=0;
    for(int u=1;u<=n*2;u++)son[u].clear();
    for(int i=1;i<=m;i++){
        scanf("%d %d %d %d",&e[i].u,&e[i].v,&e[i].l,&e[i].h);
        add(e[i].u,e[i].v,e[i].l);
        add(e[i].v,e[i].u,e[i].l);
    }
    Dijkstra();
    for(int u=1;u<=2*n;u++)fa[u]=u;
    for(int u=1;u<=n;u++){
        sz[u]=1;
        minD[u]=dst[u];
    }
    int nV=n;
    sort(e+1,e+1+m,cmpE);
    for(int i=1;i<=m;i++){
        int ru=root(e[i].u);
        int rv=root(e[i].v);
        if(ru==rv)continue;
        nV++;
       fa[ru]=fa[rv]=nV;
        height[nV]=e[i].h;
        minD[nV]=min(minD[ru],minD[rv]);
        son[nV].push_back(ru);
        son[nV].push_back(rv);
    }
    L=log(n)/log(2)+1;
    dfs(nV,0);
    int k,s;
    int v,p;
    int ans=0;
    scanf("%d %d %d",&nQ,&k,&s);
    for(int iQ=1;iQ<=nQ;iQ++){
        scanf("%d %d",&v,&p);
        v=(v+k*ans-1)%n+1;
        p=(p+k*ans)%(s+1);
        ans=query(p,v);
        printf("%d\n",ans);
    }
}

int main(){
    freopen("return.in","r",stdin);
    freopen("return.out","w",stdout);
    
    int T;
    scanf("%d",&T);
    for(int t=1;t<=T;t++)
        solve();
}
posted @ 2023-07-12 14:56  hsfzbzjr  阅读(19)  评论(0)    收藏  举报