【BFS树】“范式杯”2023牛客暑期多校训练营1 K

K-Subdivision_“范式杯”2023牛客暑期多校训练营1 (nowcoder.com)

题意:

【BFS树】“范式杯”2023牛客暑期多校训练营1 K_第1张图片

思路:

首先对1结点进行BFS,形成一棵BFS树

贡献由两部分组成

第一部分由BFS树的非树边组成

第二部分由树边组成

对于第一部分,对于一个结点和该结点相邻的非树边上产生的有效结点数最多为 K-dis[u]

该结点可能会有很多条非树边,因此要先统计该结点非树边的个数cnt

对于这个结点产生的所有非树边,产生的贡献为(K-dis[u])*cnt+1

最后的+1是算上它本身

对于第二部分,有一个结论就是,把结点加在叶子结点对应的边上最优

证明如下:

【BFS树】“范式杯”2023牛客暑期多校训练营1 K_第2张图片

因此我们对每个叶子结点去算贡献

对于一个叶子结点,贡献也为(K-dis[u])*cnt+1

只不过对于叶子结点,有贡献的边至少为1,因此需要cnt=max(cnt,1)

样例解释:

【BFS树】“范式杯”2023牛客暑期多校训练营1 K_第3张图片

 

其余的在代码中给出了解释:

#include 

#define int long long

using namespace std;

const int mxn=1e5+10;
const int mxe=1e5+10;

struct ty{
    int to,next;
}edge[mxe<<2];

int N,M,K,u,v;
int tot=0;
int head[mxn],dis[mxn],inner[mxn],pre[mxn];

void add(int u,int v){
    edge[tot].to=v;
    edge[tot].next=head[u];
    head[u]=tot++;
}
void G_init(){
    tot=0;
    for(int i=0;i<=N;i++){
        head[i]=-1;
    }
}
void bfs(){
    memset(dis,-1,sizeof(dis));
    queue Q;
    Q.push(1);
    dis[1]=0;
    while(!Q.empty()){
        int u=Q.front();
        Q.pop();
        for(int i=head[u];~i;i=edge[i].next){
            if(dis[edge[i].to]==-1){
                dis[edge[i].to]=dis[u]+1;
                pre[edge[i].to]=u;//判断是否为非树边
                inner[u]=1;//标记是否是BFS树的叶子结点
                Q.push(edge[i].to);
            }
        }
    }
}
void solve(){
    cin>>N>>M>>K;
    G_init();
    for(int i=1;i<=M;i++){
        cin>>u>>v;
        add(u,v);
        add(v,u);
    }
    bfs();//建立BFS树
    int ans=1;//算上1结点
    for(int u=2;u<=N;u++){
        if(dis[u]==-1||dis[u]>K) continue;//对于不在BFS树上的点,或者在树上离1结点太远的点,都没有贡献
        int cnt=0;
        for(int i=head[u];~i;i=edge[i].next){
            if(pre[edge[i].to]==u||pre[u]==edge[i].to) continue;//判断是否为非树边
            cnt++;//统计有效边,对于非树边那就是非树边的个数
        }
        if(!inner[u]) cnt=max(cnt,1ll);//如果为叶子结点,有效边至少为1,就是叶子结点对应的那条边
        ans+=(K-dis[u])*cnt+1;//计算贡献
    }
    cout<>__;
    while(__--)solve();return 0;
}

 

你可能感兴趣的:(图论,暑假多校,宽度优先,算法)