题目描述

样例

样例输入

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. nginx 1.12安装

    准备工作 使用root用户安装. 到nginx官网下载Linux源码或者执行:wget http://nginx.org/download/nginx-1.12.2.tar.gz. 到pcre站点下载 ...

  2. mysql-1-select

    #进阶1:基础查询 /* 语法: SELECT 查询列表 FROM 表名; 特点: 1.查询列表可以是:表中字段.常量值.表达式.函数 2.查询的结果是一个虚拟的表格 */ USE myemploye ...

  3. Pyinstaller打包通用流程

    Pyinstaller打包通用流程 前言 什么是Pyinstaller Pyinstaller是用于打包python项目的一个工具, 可以将项目代码打包成可执行文件, 在其他机器上使用. 通俗的说, ...

  4. 【音乐爬虫】Python爬虫-selenium+browsermob-proxy 解决动态网页 js渲染问题

    1.一般的python爬虫很简单,直接请求对应网址,解析返回的数据即可,但是有很多网站的数据的js动态渲染的,你直接请求是得不到对应的数据的 这时就需要其它手段来处理了. 2.以一个例子来说明,整个过 ...

  5. 可能是东半球第二好用的软件工具全部在这里(update in 2020.10.09)

    1. 产品经理工具种草 浏览器:Google Chrome 网络浏览器 原型绘制软件:墨刀- 在线产品原型设计与协作平台(https://modao.cc/).摹客mockplus - 摹客,让设计和 ...

  6. cobbler自动化安装centos

    转载于:https://www.cnblogs.com/skymydaiji/p/10877533.html 一.cobbler介绍 1.前言 cobbler 是基于 python 语言开发的 pxe ...

  7. turtle库元素语法分析

    一.turtle原理理解: turtle库是Python中一个有趣的图形绘制函数库.原名(海龟),我们想象一只海龟,位于显示器上窗体的正中心,在画布上游走,它游走的轨迹就形成了绘制的图形. 对于小海龟 ...

  8. 多测师讲解_ 高级自动化测试selenium_001基本学习

    高级自动化测试python+selenium教程手册 --高级讲师肖sir 第 1 章webdriver 环境搭建好了,我们正式学习 selenium 的 webdriver 框架,它不像 QTP 之 ...

  9. Django 中实现连接多个数据库并实现读写分离

    读写分离 其基本原理就是让主数据库处理事务性增,改,删操作(INSERT,UPDATE,DELETE)操作,而从数据库处理SELECT查询操作,数据库复制被用来把事物性操作导致的变更同步到其他从数据库 ...

  10. 50种编程语言,一句 “Hello, World”!展现编程语言七十年发展!

    mod confinment { use std::os::raw::{c_char}; extern "C" { pub fn puts(txt: *const c_char); ...