题目描述

样例

样例输入

8 5
BBWWWBBW
1 2
2 3
4 5
6 7
7 8

样例输出

84
2
1 4

样例解释





分析

首先,我们要预处理出一个点到该联通块内所有点的距离之和 \(f\)

这个东西用换根 \(DP\) 搞一下就可以了

那么这个联通块内所有点对的距离之和就是这个联通块所有点的 \(f\) 值之和除以 \(2\)

除以 \(2\) 是因为点对是无序的

对于子任务一:

当联通块的个数为 \(2\) 时,两个联通块内的贡献我们已经考虑了

我们需要考虑的就是跨过联通块的贡献

我们设从联通块 \(1\) 中选择的点为 \(a\),从联通块 \(2\) 中选择的点为 \(b\) ,联通块的大小是 \(cnt\)

那么贡献就是 \((f[a]+cnt[1])*(n-cnt[1])+cnt[1]*f[b]\)

前半部分统计的是 \(1\) 联通块经过 \((a,b)\) 这条边的贡献

后半部分统计的是 \(2\) 联通块对 \(1\) 联通块的贡献

显然,我们需要把两个联通块内 \(f\) 值最大的点连接起来

当联通块的个数为 \(3\) 时,我们可以枚举哪个联通块在中间

设第一个联通块与第二个联通块通过 \((x, y)\) 相连

第二个联通块与第三个联通块通过 \((u, v)\) 相连。

则联通块与联通块之间的贡献为:

\((f[x]+cnt[1])(n−cnt[1])+(f[v]+cnt[3])(n−cnt[3])+cnt[1]f[y]+cnt[3]f[u]+dis(y,u)cnt[1]cnt[3]\)

其中 \(dis\) 代表两点间的距离

那么 \(x\), \(v\) 应该是联通块 \(1\) 和联通块 \(3\) 中 \(f\) 最大的点。

对于联通块 \(2\),我们只要求出 \(cnt[1]f[y]+cnt[3]f[u]+dis(y,u)cnt[1]cnt[3]\) 的最大值即可,这个

可以通过 \(dp\) 实现

我们分别开两个数组存储当前 \(cnt[1]f[y]\) 和 \(cnt[3]f[u]\)的最大值

自底向上 \(dp\)

对于后面的 \(dis\) 值,我们只需要在向上递归是加一个 \(cnt[1]cnt[3]\) 即可

对于子任务二:

考虑一棵树内的所有不满足条件的点。如果有奇数个这样的点,那么无解,否则一定有解,

并且唯一。

我们要使这些点变成合法的,就需要对它们进行两两匹配,然后改变每一对点路径上所有

边的存在情况。

那么,如果一条边两侧的连通块内有奇数个这样的点,这个边的状态就一定被改变了奇数

次,因此它被删掉了;否则它没有被删掉。

总复杂度 \(O(n)\)

代码

#include<cstdio>
#include<cstring>
#include<iostream>
#define rg register
inline int read(){
rg int x=0,fh=1;
rg char ch=getchar();
while(ch<'0' || ch>'9'){
if(ch=='-') fh=-1;
ch=getchar();
}
while(ch>='0' && ch<='9'){
x=(x<<1)+(x<<3)+(ch^48);
ch=getchar();
}
return x*fh;
}
const int maxn=1e6+5;
int n,m,sl,h[maxn],tot=1,rt1,rt2,rt3,siz[maxn],cnt[maxn],vis[maxn];
struct asd{
int to,nxt;
}b[maxn];
void ad(int aa,int bb){
b[tot].to=bb;
b[tot].nxt=h[aa];
h[aa]=tot++;
}
char s[maxn];
long long f[maxn],g[maxn];
void dfs(int rt,int now,int fa){
siz[now]=1;
vis[now]=rt;
cnt[rt]++;
for(rg int i=h[now];i!=-1;i=b[i].nxt){
rg int u=b[i].to;
if(u==fa) continue;
dfs(rt,u,now);
siz[now]+=siz[u];
g[now]+=g[u]+siz[u];
}
}
void dfs2(int rt,int now,int fa){
if(now==rt)f[now]=g[now];
for(rg int i=h[now];i!=-1;i=b[i].nxt){
rg int u=b[i].to;
if(u==fa) continue;
f[u]=f[now]+cnt[rt]-siz[u]-siz[u];
dfs2(rt,u,now);
}
}
int jl,mmax,A,B,C,D;
void solve1(){
rg long long ans=0;
for(rg int i=1;i<=n;i++){
if(vis[i]==0){
dfs(i,i,0);
if(!rt1) rt1=i;
else rt2=i;
}
}
dfs2(rt1,rt1,0);
dfs2(rt2,rt2,0);
for(rg int i=1;i<=n;i++){
ans+=f[i];
}
ans/=2;
mmax=-1,jl=0;
for(rg int i=1;i<=n;i++){
if(vis[i]==rt1){
if(f[i]>mmax){
mmax=f[i];
jl=i;
}
}
}
A=jl;
mmax=-1,jl=0;
for(rg int i=1;i<=n;i++){
if(vis[i]==rt2){
if(f[i]>mmax){
mmax=f[i];
jl=i;
}
}
}
B=jl;
ans+=(f[A]+cnt[rt1])*(n-cnt[rt1])+cnt[rt1]*f[B];
printf("%lld\n",ans);
}
long long maxb[maxn],maxc[maxn],haha=0;
void dfs5(int now,int fa,int cntl,int cntr){
maxb[now]=cntl*f[now];
maxc[now]=cntr*f[now];
for(rg int i=h[now];i!=-1;i=b[i].nxt){
rg int u=b[i].to;
if(u==fa) continue;
dfs5(u,now,cntl,cntr);
haha=std::max(haha,maxb[now]+maxc[u]+1LL*cntl*cntr);
haha=std::max(haha,maxc[now]+maxb[u]+1LL*cntl*cntr);
maxb[now]=std::max(maxb[u]+1LL*cntl*cntr,maxb[now]);
maxc[now]=std::max(maxc[u]+1LL*cntl*cntr,maxc[now]);
}
}
long long js(int l,int mids,int r){
memset(maxb,0,sizeof(maxb));
memset(maxc,0,sizeof(maxc));
rg long long jla=0,jld=0;
haha=0;
for(rg int i=1;i<=n;i++){
if(vis[i]==l){
if(f[i]>jla) jla=f[i];
}
if(vis[i]==r){
if(f[i]>jld) jld=f[i];
}
}
dfs5(mids,0,cnt[l],cnt[r]);
haha+=(jla+cnt[l])*(n-cnt[l])+(jld+cnt[r])*(n-cnt[r]);
return haha;
}
void solve2(){
rg long long ans=0;
for(rg int i=1;i<=n;i++){
if(vis[i]==0){
dfs(i,i,0);
if(!rt1) rt1=i;
else if(!rt2)rt2=i;
else rt3=i;
}
}
dfs2(rt1,rt1,0);
dfs2(rt2,rt2,0);
dfs2(rt3,rt3,0);
for(rg int i=1;i<=n;i++){
ans+=f[i];
}
ans/=2;
rg long long nans=0;
nans=std::max(nans,js(rt1,rt2,rt3));
nans=std::max(nans,js(rt1,rt3,rt2));
nans=std::max(nans,js(rt2,rt1,rt3));
ans+=nans;
printf("%lld\n",ans);
}
int sta[maxn],tp,du[maxn],num[maxn];
bool kil[maxn];
void dfs3(int now,int fa){
for(rg int i=h[now];i!=-1;i=b[i].nxt){
rg int u=b[i].to;
if(u==fa) continue;
dfs3(u,now);
num[now]+=num[u];
}
}
void dfs4(int rt,int now,int fa){
for(rg int i=h[now];i!=-1;i=b[i].nxt){
rg int u=b[i].to;
if(u==fa) continue;
dfs4(rt,u,now);
if((num[rt]-num[u])&1 && num[u]&1){
kil[i]=1;
}
}
}
void solve3(){
for(rg int i=1;i<=n;i++){
if(s[i]=='B'){
if(du[i]%2==0) num[i]=1;
} else {
if(du[i]&1) num[i]=1;
}
}
if(sl==2){
dfs3(rt1,0);
dfs3(rt2,0);
if(num[rt1]&1 || num[rt2]&1){
printf("-1\n");
return;
}
dfs4(rt1,rt1,0);
dfs4(rt2,rt2,0);
} else {
dfs3(rt1,0);
dfs3(rt2,0);
dfs3(rt3,0);
if(num[rt1]&1 || num[rt2]&1 || num[rt3]&1){
printf("-1\n");
return;
}
dfs4(rt1,rt1,0);
dfs4(rt2,rt2,0);
dfs4(rt3,rt3,0);
}
for(rg int i=1;i<tot;i+=2){
if(kil[i] || kil[i+1]) continue;
sta[++tp]=(i+1)/2;
}
printf("%d\n",tp);
for(rg int i=1;i<=tp;i++){
printf("%d ",sta[i]);
}
printf("\n");
}
int main(){
freopen("lct.in","r",stdin);
freopen("lct.out","w",stdout);
memset(h,-1,sizeof(h));
n=read(),m=read();
sl=(n-m);
scanf("%s",s+1);
rg int aa,bb;
for(rg int i=1;i<=m;i++){
aa=read(),bb=read();
ad(aa,bb);
ad(bb,aa);
du[aa]++;
du[bb]++;
}
if(sl==2){
solve1();
} else {
solve2();
}
solve3();
return 0;
}

联考day7 C. 树和森林 树形DP的更多相关文章

  1. LOJ3053 十二省联考2019 希望 容斥、树形DP、长链剖分

    传送门 官方题解其实讲的挺清楚了,就是锅有点多-- 一些有启发性的部分分 L=N 一个经典(反正我是不会)的容斥:最后的答案=对于每个点能够以它作为集合点的方案数-对于每条边能够以其两个端点作为集合点 ...

  2. 可恶!学了这么久的LCA,联考的题目却是LCA+树形DP!!!可恶|!!!这几天想学学树形DP吧!先来一道入门题HDU 1520 Anniversary party

    题目描述 某大学有N个职员,编号为1~N.他们之间有从属关系,也就是说他们的关系就像一棵以校长为根的树,父结点就是子结点的直接上司.现在有个周年庆宴会,宴会每邀请来一个职员都会增加一定的快乐指数Ri, ...

  3. P3748 [六省联考2017]摧毁“树状图”

    传送门 显然是可以树形 $dp$ 的 对每个节点维护以下 $5$ 个东西 $1.$ 从当前节点出发往下的链的最大贡献 $2.$ 节点子树内不经过本身的路径最大贡献 $3.$ 节点子树内经过本身的路径的 ...

  4. 2014 Super Training #9 E Destroy --树的直径+树形DP

    原题: ZOJ 3684 http://acm.zju.edu.cn/onlinejudge/showProblem.do?problemCode=3684 题意: 给你一棵树,树的根是树的中心(到其 ...

  5. (中等) HDU 5293 Tree chain problem,树链剖分+树形DP。

    Problem Description   Coco has a tree, whose vertices are conveniently labeled by 1,2,…,n.There are ...

  6. bzoj 4871: [Shoi2017]摧毁“树状图” [树形DP]

    4871: [Shoi2017]摧毁"树状图" 题意:一颗无向树,选两条边不重复的路径,删去选择的点和路径剩下一些cc,求最多cc数. update 5.1 : 刚刚发现bzoj上 ...

  7. BZOJ1758[Wc2010]重建计划——分数规划+长链剖分+线段树+二分答案+树形DP

    题目描述 输入 第一行包含一个正整数N,表示X国的城市个数. 第二行包含两个正整数L和U,表示政策要求的第一期重建方案中修建道路数的上下限 接下来的N-1行描述重建小组的原有方案,每行三个正整数Ai, ...

  8. 算法笔记--树的直径 && 树形dp && 虚树 && 树分治 && 树上差分 && 树链剖分

    树的直径: 利用了树的直径的一个性质:距某个点最远的叶子节点一定是树的某一条直径的端点. 先从任意一顶点a出发,bfs找到离它最远的一个叶子顶点b,然后再从b出发bfs找到离b最远的顶点c,那么b和c ...

  9. 【bzoj5123】[Lydsy12月赛]线段树的匹配 树形dp+记忆化搜索

    题目描述 求一棵 $[1,n]$ 的线段树的最大匹配数目与方案数. $n\le 10^{18}$ 题解 树形dp+记忆化搜索 设 $f[l][r]$ 表示根节点为 $[l,r]$ 的线段树,匹配选择根 ...

随机推荐

  1. spark 四种模式

    Spark 三种运行模式  一:Spark On Local     此种模式下,我们只需要在安装Spark时不进行hadoop和Yarn的环境配置,只要将Spark包解压即可使用,运行时Spark目 ...

  2. Vue学习笔记使用系列一【表单】

    脚手架的搭建,请查看另外一篇日记:https://www.cnblogs.com/Fengge518/p/11837078.html 1:直接代码了 1 <!DOCTYPE html> 2 ...

  3. 使用Redis做消息队列

    基于内存的单线程数据库,使Redis的线程安全性极高.而Redis的双向链表数据类型(List)天生就可作为消息队列存储消息. 在这里就不说消息队列的等等一些优点.但是补充一下Redis的List类型 ...

  4. Spring循环依赖的问题

      什么是循环依赖?就是两个Bean相互引用,比如用@Autowire 相互注入.   那么Spring是如何解决这个问题的呢?在Bean还未完全实例化前(类只实例化了一部分),将bean提前暴露出来 ...

  5. 安卓app功能或自动化测试覆盖率统计(不用instrumentation启动app)

    一文带你揭秘如何采取非instrumentation启动app,打造实时统计覆盖率,一键触发覆盖率测试报告. 在上篇文章,一文带你解决Android app手工测试或者自动化测试覆盖率统计(撸代码版) ...

  6. 089 01 Android 零基础入门 02 Java面向对象 02 Java封装 01 封装的实现 03 # 088 01 Android 零基础入门 02 Java面向对象 02 Java封装 01 封装的实现 03 使用包进行类管理(1)——创建包

    089 01 Android 零基础入门 02 Java面向对象 02 Java封装 01 封装的实现 03 # 088 01 Android 零基础入门 02 Java面向对象 02 Java封装 ...

  7. 022 01 Android 零基础入门 01 Java基础语法 03 Java运算符 02 算术运算符

    022 01 Android 零基础入门 01 Java基础语法 03 Java运算符 02 算术运算符 本文知识点:Java中的算术运算符 算术运算符介绍 算术运算符代码示例 注意字符串连接问题和整 ...

  8. matlab中subplot 在平铺位置创建坐标区

    来源:https://ww2.mathworks.cn/help/matlab/ref/subplot.html?searchHighlight=subplot&s_tid=doc_srcht ...

  9. java高级项目 jdbc与数据库连接数据库

    //图书管类 public class Book { private Integer id; private String b_name; private double b_price; privat ...

  10. TP5本地运行正常,线上运行某页面出现【模板文件不存在】问题的解决办法

    相信许多小伙伴和我一样,明明在本地运行页面一切正常,而到线上(本人是用的虚拟主机)出现了如下图的问题: 其实这个问题出现的原因很简单,就是我们开发是在windows 系统下,windows系统对大小写 ...