题目链接:https://zhixincode.com/contest/14/problem/D?problem_id=206

样例输入 1 

5 5
1 2 1
1 3 1
2 4 1
2 5 1
1 5 1

样例输出 1

20

样例输入 2 

5 10
1 2 1
1 3 2
1 4 3
1 5 4
2 3 5
2 4 6
2 5 7
3 4 8
3 5 9
4 5 10

样例输出 2

146

题解:

首先,删除一条边不可能使得任意两座城市的最短距离变得更近,所以尽可能地多删除边不会有任何坏处,因此最后得到的道路系统应该就是一棵树。

(吉老师表示,这道题是生成树上状压dp的经典套路。我这只蒟蒻听得一愣一愣的,感觉自己dp确实太薄弱了……)

jls表示套路是:用 $dp[i][S]$ 来表示一棵子树的某些属性,这棵子树的所有节点组成点集 $S$,并且该子树以 $i$ 节点为根。

首先将要计算的 $\sum_{i=1}^{n} \sum_{j=i+1}^{n} d(i,j)$ 转变成另一种计算方法,考虑一条边 $(u_i,v_i,w_i)$ 对这个答案的贡献;

考虑到一棵树上任意两个点间的路径都是唯一的,所以最短路就是唯一的那一条路径。因此一条边 $(u_i,v_i,w_i)$ 在 $\sum_{i=1}^{n} \sum_{j=i+1}^{n} d(i,j)$ 中被计算几次,就等于以这条边两端的两个子树内节点数的乘积。我们把这个值叫做这条边的贡献。

然后用 $dp[i][S]$ 表示在点集 $S$ 上构造了一棵根为 $i$ 的生成树,它的所有边的贡献之和是最大的。我们不需要关心树的具体结构,我们只要知道在点集 $S$ 上可以搞出一棵生成树,这棵树的复杂程度是最大就好了。

那么如何进行转移呢?很容易想到需要枚举子集,我们可以枚举 $S$ 的真子集 $T$,显然由于 $|T|<|S|$,所以对于任意的 $j \in T$,$dp[j][T]$ 肯定已经被计算好了;同样的,对于集合 $S-T$,对于任意的 $i \in S-T$,$dp[i][S-T]$ 肯定也是计算好了的。除非这是一个非法状态,换句话说即在原图上 $T$ 或者 $S-T$ 是不连通的。

那么,状态转移方程即:对于在原图中存在的 $\forall edge(i,j)$,其中 $i \in S-T, j \in T$,有 $dp[i][S] = max(dp[i][S],dp[j][T]+dp[i][S-T]+ w(i,j) \cdot |T| \cdot |n-T|)$。(这里有一个易错的点是误认为是 $dp[j][T]+dp[i][S-T]+ w(i,j) \cdot |T| \cdot |S-T|$,错误的理由是 $S$ 并不是全集 $V$)

注:这个状压dp涉及到枚举子集的运算, x = x & (x-) 是把 $x$ 二进制下最靠右的第一个 $1$ 变为 $0$, for(int t=s;t;t=(t-)&s){} 则可以枚举 $s$ 的子集 $t$,而对于 $s$ 的一个子集 $t$ 求其补集 $s_t$ 则可以用 s_t = s ^ t 。

AC代码:

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int maxn=; int n,m;
ll mp[maxn][maxn];
ll dp[maxn][<<maxn];
vector<int> poi[<<maxn]; int main()
{
ios::sync_with_stdio();
cin.tie(), cout.tie(); cin>>n>>m;
for(int i=,u,v,w;i<=m;i++)
{
cin>>u>>v>>w;
mp[u][v]=mp[v][u]=(ll)w;
} memset(dp,-,sizeof(dp));
for(int s=;s<(<<n);s++)
{
for(int i=;i<=n;i++) if(s&(<<(i-))) poi[s].push_back(i);
if(poi[s].size()==) dp[poi[s].front()][s]=; //对于只有一个点的状态进行初始化
} for(int s=;s<(<<n);s++)
{
for(int t=(s-)&s;t;t=(t-)&s)
{
if(dp[poi[t].front()][t]==-) continue; //在原图上集合T不连通
if(dp[poi[s-t].front()][s-t]==-) continue; //在原图上集合S-T不连通
for(auto i:poi[s-t])
{
for(auto j:poi[t])
{
if(!mp[i][j]) continue;
ll T=poi[t].size();
dp[i][s]=max(dp[i][s],dp[j][t]+dp[i][s-t]+T*(n-T)*mp[i][j]);
}
}
}
} ll ans=;
for(int i=;i<=n;i++) ans=max(ans,dp[i][(<<n)-]);
cout<<ans<<endl;
}

CCPC-Wannafly Winter Camp Day3 Div1 - 精简改良 - [生成树][状压DP]的更多相关文章

  1. 【CCPC-Wannafly Winter Camp Day3 (Div1) D】精简改良(状压DP)

    点此看题面 大致题意: 给你一张图,定义\(dis(i,j)\)为\(i\)与\(j\)的最短距离,现要求删去若干条边,使得图仍然联通,且\(\sum_{i=1}^n\sum_{j=i+1}^ndis ...

  2. 2020 CCPC Wannafly Winter Camp Day1 C. 染色图

    2020 CCPC Wannafly Winter Camp Day1 C. 染色图 定义一张无向图 G=⟨V,E⟩ 是 k 可染色的当且仅当存在函数 f:V↦{1,2,⋯,k} 满足对于 G 中的任 ...

  3. Wannafly Winter Camp Day8(Div1,onsite) E题 Souls-like Game 线段树 矩阵乘法

    目录 Catalog Solution: (有任何问题欢迎留言或私聊 && 欢迎交流讨论哦 Catalog @ Problem:传送门  Portal  原题目描述在最下面.  简单的 ...

  4. CCPC-Wannafly Winter Camp Day3 Div1 - 石头剪刀布 - [带权并查集]

    题目链接:https://zhixincode.com/contest/14/problem/I?problem_id=211 样例输入 1  3 5 2 1 1 2 1 2 1 1 2 3 2 1 ...

  5. CCPC-Wannafly Winter Camp Day3 Div1 - 排列

    题目链接:https://zhixincode.com/contest/14/problem/A?problem_id=203 time limit per test: 1 secondmemory ...

  6. 【CCPC-Wannafly Winter Camp Day3 (Div1) G】排列(水题)

    点此看题面 大致题意:已知 \(p\)为\(n\)的一个排列,定义\(A(p)_i=min_{j=1}^ip_j\),若用\(q_i\)表示\(p\)第\(i\)小的前缀的长度(以值为第一关键字,下标 ...

  7. 【CCPC-Wannafly Winter Camp Day3 (Div1) F】小清新数论(莫比乌斯反演+杜教筛)

    点此看题面 大致题意: 让你求出\(\sum_{i=1}^n\sum_{j=1}^n\mu(gcd(i,j))\). 莫比乌斯反演 这种题目,一看就是莫比乌斯反演啊!(连莫比乌斯函数都有) 关于莫比乌 ...

  8. 【CCPC-Wannafly Winter Camp Day3 (Div1) I】石头剪刀布(按秩合并并查集)

    点此看题面 大致题意: 有\(n\)个人,第\(i\)个人坐在编号为\(i\)的座位上,每个人等概率有石头.剪刀.布中的一张卡片.有两种操作:第一种是第\(y\)个人挑战第\(x\)个人,如果胜利则\ ...

  9. CCPC Wannafly Winter Camp Div2 部分题解

    Day 1, Div 2, Prob. B - 吃豆豆 题目大意 wls有一个\(n\)行\(m\)列的棋盘,对于第\(i\)行第\(j\)列的格子,每过\(T[i][j]\)秒会在上面出现一个糖果, ...

随机推荐

  1. 关于ř与tableau的集成---- k均值聚类

    1.利用R内置数据集iris: 2.通过Rserve 包连接tableau,服务器:localhost,默认端口6311: 3.加载数据集iris: 4.编辑字段:Cluster <span s ...

  2. 【转载】JAVA基础:注解

    原文:https://www.cnblogs.com/xdp-gacl/p/3622275.html#undefined 一.认识注解 注解(Annotation)很重要,未来的开发模式都是基于注解的 ...

  3. android用TextView实现跑马灯效果

    今天搞啦很久,其实很简单,就加几个属性就可以啦! 图如下 : 有的说要重写TextView方法,有的说要设置固定长度,但是我没重写也没有设置固定长度也弄出来啦!跑在2.3.3的手机上面.就是不知道其他 ...

  4. Shell脚本编程(一):初识shell script

    Shell简介 Shell是一个命令解释器,它是一个用 C 语言编写的程序,它是用户使用 Linux 的桥梁.Shell 是指一种应用程序,这个应用程序提供了一个界面,用户通过这个界面访问操作系统内核 ...

  5. 2.4 Apache Axis2 快速学习手册之XMLBeans 构建Web Service

    4. 使用XMLBeans生成服务(通过xml bean 命令将wsdl 文件生成java 代码) 要使用XMLBeans生成服务,请执行以下步骤. 通过在Axis2_HOME / samples / ...

  6. [svc]linux的ip命令操作接口和路由表

    参考: https://www.tecmint.com/ip-command-examples/ 学会linux的配置ip,配置网关,添加路由等命令 man ip man ip address man ...

  7. [svc]find+xargs/exec重命名文件后缀&文件操作工具小结

    30天内的文件打包 find ./test_log -type f -mtime -30|xargs tar -cvf test_log.tar.gz awk运算-解决企业统计pv/ip问题 find ...

  8. Socket网络编程--聊天程序(6)

    这一小节将增加一个用户的结构体,用于保存用户的用户名和密码,然后发给服务器,然后在服务器进行判断验证.这里就有一个问题,以前讲的就是发送字符串是使用char类型进行传输,然后在服务器进行用同样是字符串 ...

  9. 【Java虚拟机】浅谈Java虚拟机

    跨平台 Java的一大特性是跨平台,而Java是如何做到跨平台的呢? 主要依赖Java虚拟机,具体来说,是Java虚拟机在各平台上的实现. Java虚拟机在不同的平台有不同的实现.同一份字节码,通过运 ...

  10. 模仿Semaphore自定义自己的 信号量

    简介 这里模仿Semaphore,自定义自己的信号量,利用AQS共享模式 1.MySemaphore.java package com.jacky; import java.util.concurre ...