1 条题解

  • 0
    @ 2025-8-24 22:40:31

    自动搬运

    查看原文

    来自洛谷,原作者为

    avatar xieyikai2333
    此时此刻的光辉,盼君勿忘。

    搬运于2025-08-24 22:40:31,当前版本为作者最后更新于2022-10-23 21:09:38,作者可能在搬运后再次修改,您可在原文处查看最新版

    自动搬运只会搬运当前题目点赞数最高的题解,您可前往洛谷题解查看更多

    以下是正文



    闲话

    • 赛时爆肝 T2,结果 T2 是个紫的没打出来,然后这题没时间了,打了个 2020 分的暴力滚蛋了。

    解题思路

    • 显然,我们先进行操作 1 再进行操作 2 一定不劣,否则操作 2 连上去的边可能又被炸掉。

    • 考虑把森林炸成若干链,然后连起来,显然如果我们炸了 xx 条边,那么操作 2 的次数可以表示为 (n1)(mx)(n-1)-(m-x)。于是我们只要使炸掉的边数(xx)+炸掉的点数(操作 1 的次数)最小即可。

    • 考虑对于每个无根树随便取个根进行树形DP

    状态设计

    • dpi,0/1/2dp_{i,0/1/2} 表示 ii 的子树已经被炸成若干链了且 $\begin{cases} \text{这个点被炸了} &0\\ \text{这个点没有被炸,它的儿子有 1 个连着它} &1\\ \text{这个点没有被炸,它的儿子有 2 个连着它} &2 \end{cases}$

    状态转移

    • 约定:记 sonuson_u 为点 uu 的儿子集合,dud_u 为点 uu 的度数,FIRsthFIR_{sth} 是一类数中的最大值,SECsthSEC_{sth} 是一类数中的次大值。

    • 对于状态 00

      • 转移方程:$$dp_{u,0}=\sum_{v \in son_{u}} \min\{dp_{v,0}-1,dp_{v,2}\}+d_{u}+1 $$
      • 解释说明:
        • 如果一个点被炸掉,那么它的子树无论何种情况都可以转移。
        • 之所以 dpv,0dp_{v,0} 要减去 11 是因为如果 uuvv 都炸它们之间的边被重复计算了。
        • 之所以不用考虑 dpv,1dp_{v,1},是因为 dpv,1dp_{v,1} 一定大于 dpv,2dp_{v,2},原因由后面会给出的 dpv,2dp_{v,2} 的转移方程可知。
        • 加上 dud_{u} 是因为又炸了 dud_{u} 条边,加上 11 是因为又炸了一个点
    • 对于状态 11

      • 转移方程:$$dp_{u,1}=(\sum_{v \in son_{u}} dp_{v,0})-\max\{0,FIR_{v \in son_{u}}(dp_{v,0}-dp_{v,1})\} $$
      • 解释说明:
        • 由定义,uu 的儿子最多有一个不被炸,所以最多有一个儿子状态可以是 11 其他都必须被炸。
    • 对于状态 22

      • 转移方程:$$dp_{u,2}=dp_{i,1}-\max\{0,SEC_{v \in son_{u}}(dp_{v,0}-dp_{v,1})\} $$
      • 解释说明:
        • 由定义,uu 的儿子最多有两个不被炸。在有一个不被炸的基础上再减去第二大的即可。
      • 由转移方程可知,dpu,2<dpu,1dp_{u,2} \lt dp_{u,1} 显然成立。

    最终答案

    • 约定:ANSANS 是最终答案,rootroot 是每棵树的根节点组成的集合。
    ANS=urootmin{dpu,0,dpu,2}ANS=\sum_{u \in root}\min\{dp_{u,0},dp_{u,2}\}

    至于为什么不用管 dpu,1dp_{u,1},不用我多说了吧?


    代码实现

    好像没什么要注意的细节。。。

    AC 代码

    #include <bits/stdc++.h>
    using namespace std;
    const int N=2e6+5;
    int dp[N][3];
    bool vis[N];
    vector<int> nodes[N];
    void dfs(int u)
    {
    	vis[u]=true;
    	int fir=0,sec=0;
    	for(int v:nodes[u])
    	{
    		if(vis[v])continue;
    		dfs(v);
    		int delta=dp[v][0]-dp[v][1];
    		if(delta>fir)sec=fir,fir=delta;
    		else if(delta>sec)sec=delta;
    		dp[u][0]+=min(dp[v][0]-1,dp[v][2]);
    		dp[u][1]+=dp[v][0];
    	}
    	dp[u][0]+=nodes[u].size()+1;
    	dp[u][1]-=fir;
    	dp[u][2]=dp[u][1]-sec;
    	return;
    }
    int main()
    {
    	int n,m;
    	scanf("%d %d",&n,&m);
    	int ans=(n-1)-m;
    	for(int i=1;i<=m;i++)
    	{
    		int u,v;
    		scanf("%d %d",&u,&v);
    		nodes[u].push_back(v);
    		nodes[v].push_back(u);
    	}
    	for(int i=1;i<=n;i++)if(!vis[i])dfs(i),ans+=min(dp[i][0],dp[i][2]);
    	printf("%d",ans);
    	return 0;
    }
    
    • 1

    信息

    ID
    7688
    时间
    1000ms
    内存
    512MiB
    难度
    5
    标签
    递交数
    0
    已通过
    0
    上传者