模拟赛:树和森林(lct.cpp) (树形DP,换根DP好题)
题面



题解
先解决第一个子问题吧,它才是难点
Subtask_1
我们可以先用一个简单的树形DP处理出每棵树内部的dis和,记为dp0[i],
然后再用一个换根的树形DP处理出每棵树内点 i 到树内每个点的距离和,记为dp[i],
好,现在分两个连通块跟三个连通块两种情况讨论
两个连通块
把两棵树A,B合并到一起,我们得先确定两个连接的点,
若其分别为 i,j,不难发现答案就是 dp0[A] + dp0[B] + dp[i] * size[B] + dp[j] * size[A] + size[A] * size[B]
其中,dp0[A],dp0[B],size[A],size[B]都是确定的,那么当答案取最大的时候,dp[i]、dp[j]一定分别都取最大
所以在两棵树中找dp值最大的两个点 i , j 就行了。
三个连通块
两条边连接三棵树,幸运的是,大的情况只有三种(A-B-C , A-C-B , B-A-C)

其中一棵树一定连了两条边,且不一定是同一个点连出去,不妨设中间那棵为B
注意到中间那条绿色的路径没?也就是说中间是可能有两个点的
若连接的两个点分别为 i,j,那么仔细开动一下脑筋,会发现总的贡献是
其中随着 i,j 变化的只有
因此,若以B树上每个节点为 i 考虑,我们可以用一个简单的换根DP求出上式的最大值,记为dp2[i],然后再求出B树中最大的dp2[i],加到上面三排的式子中,答案就出来了。
Subtask_2
子问题二其实更简单,不用管它的第二个条件,因为它只有0~1个解。
原图是个森林,是很多树组成,所以先考虑叶子结点,叶子结点如果是黑的,它的父边就不能删,如果是白的,它的父边就必须删;然后消除它父边的影响,再把叶子删去。这样一来,又有新的叶子,重复考虑……可以发现,最后要么无解,要么只有一个解,而且很好输出。
CODE
#include<map>
#include<queue>
#include<cmath>
#include<vector>
#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>
using namespace std;
#define MAXN 100005
#define LL unsigned long long
#define DB double
#define ENDL putchar('\n')
#define lowbit(x) ((-x)&(x))
LL read() {
LL f = 1,x = 0;char s = getchar();
while(s < '0' || s > '9') {if(s=='-')f=-f;s = getchar();}
while(s >= '0' && s <= '9') {x=x*10+(s-'0');s=getchar();}
return f*x;
}
const int MOD = 998244353;
int n,m,i,j,s,o,k;
vector<int> g[MAXN];
vector<int> id[MAXN];
char cl[MAXN];
int U[MAXN],V[MAXN];
int siz[MAXN],rt[10],cnt;
LL dp1[MAXN],dp2[MAXN],ma[MAXN],sm[MAXN],dp[MAXN];
LL dpu[MAXN],dpd[MAXN],dpa[MAXN];
vector<LL> pre[MAXN],suf[MAXN];
bool vis[MAXN],f[MAXN],ad[MAXN];
void dfs1(int x,int fa) {
vis[x] = 1;
siz[x] = 1;
dp1[x] = 0;
for(int i = 0;i < g[x].size();i ++) {
int y = g[x][i];
if(y != fa) {
dfs1(y,x);
siz[x] += siz[y];
dp1[x] += dp1[y] + (LL)siz[y];
}
}return ;
}
void dfs2(int x,int fa,int n) {
dp2[x] = 0;
if(fa) {
dp2[x] = (dp2[fa] + (dp1[fa] - (dp1[x] + (LL)siz[x]))) + (n-siz[x]);
}
dp[x] = dp1[x] + dp2[x];
ma[x] = dp[x];
sm[x] = siz[x] * (n-siz[x]);
for(int i = 0;i < g[x].size();i ++) {
int y = g[x][i];
if(y != fa) {
dfs2(y,x,n);
ma[x] = max(ma[x],ma[y]);
sm[x] += sm[y];
}
}
return ;
}
void dfs3(int x,int fa,int sizA,int sizB) {
dpd[x] = dp[x] * (LL)sizB + sizA *2ll* sizB;
LL pr = 0;
for(int i = 0;i < g[x].size();i ++) {
pre[x].push_back(pr);
int y = g[x][i];
if(y != fa) {
dfs3(y,x,sizA,sizB);
pr = max(pr,dpd[y] + sizA *1ll* sizB);
dpd[x] = max(dpd[x],dpd[y] + sizA *1ll* sizB);
}
}
pr = 0;
for(int i = (int)g[x].size()-1;i >= 0;i --) {
suf[x].push_back(pr);
int y = g[x][i];
if(y != fa) {
pr = max(pr,dpd[y] + sizA *1ll* sizB);
}
}
return ;
}
void dfs4(int x,int fa,int sizA,int sizB,int ad1,int ad2) {
dpu[x] = dp[x] * (LL)sizB + sizA *2ll* sizB;
if(fa) {
dpu[x] = max(dpu[x],max(dpu[fa],max(pre[fa][ad1],suf[fa][ad2])) + sizA *1ll* sizB);
}
dpa[x] = max(dpd[x],dpu[x]) + dp[x] * (LL)sizA;
for(int i = 0;i < g[x].size();i ++) {
int y = g[x][i];
if(y != fa) {
dfs4(y,x,sizA,sizB,i,(int)g[x].size()-1-i);
dpa[x] = max(dpa[x],dpa[y]);
}
}return ;
}
bool dfs5(int x,int fa,int ed) {
for(int i = 0;i < g[x].size();i ++) {
int y = g[x][i],idn = id[x][i];
if(y != fa) {
bool cg = dfs5(y,x,idn);
f[x] ^= cg;
}
}
if(f[x]) ad[ed] = 1;
return f[x];
}
int main() {
// freopen("lct.in","r",stdin);
// freopen("lct.out","w",stdout);
n = read();m = read();
scanf("%s",cl + 1);
for(int i = 1;i <= m;i ++) {
s = read();o = read();
U[i] = s;V[i] = o;
g[s].push_back(o);
g[o].push_back(s);
id[s].push_back(i);
id[o].push_back(i);
}
for(int i = 1;i <= n;i ++) {
if(!vis[i]) {
dfs1(i,0);
dfs2(i,0,siz[i]);
rt[++ cnt] = i;
}
}
LL ans1 = 0;
if(cnt == 2) {
ans1 = ma[rt[1]] * siz[rt[2]] + ma[rt[2]] * siz[rt[1]] + siz[rt[2]] *1ll* siz[rt[1]];
ans1 += sm[rt[1]] + sm[rt[2]];
}
else if(cnt == 3) {
LL DP1 = ma[rt[1]] * (LL)(n-siz[rt[1]]),D1 = siz[rt[1]] *1ll* (n-siz[rt[1]]);
LL DP2 = ma[rt[2]] * (LL)(n-siz[rt[2]]),D2 = siz[rt[2]] *1ll* (n-siz[rt[2]]);
LL DP3 = ma[rt[3]] * (LL)(n-siz[rt[3]]),D3 = siz[rt[3]] *1ll* (n-siz[rt[3]]);
dfs3(rt[1],0,siz[rt[2]],siz[rt[3]]);
dfs4(rt[1],0,siz[rt[2]],siz[rt[3]],0,0);
ans1 = max(ans1,dpa[rt[1]] + DP2 + DP3 + D1);
dfs3(rt[2],0,siz[rt[1]],siz[rt[3]]);
dfs4(rt[2],0,siz[rt[1]],siz[rt[3]],0,0);
ans1 = max(ans1,dpa[rt[2]] + DP1 + DP3 + D2);
dfs3(rt[3],0,siz[rt[1]],siz[rt[2]]);
dfs4(rt[3],0,siz[rt[1]],siz[rt[2]],0,0);
ans1 = max(ans1,dpa[rt[3]] + DP1 + DP2 + D3);
ans1 += sm[rt[1]] + sm[rt[2]] + sm[rt[3]];
}
printf("%lld\n",ans1);
for(int i = 1;i <= n;i ++) f[i] = (cl[i] == 'B' ? 1:0);
bool flag = 0;
for(int i = 1;i <= cnt;i ++) {
flag |= dfs5(rt[i],0,0);
}
if(flag) {
printf("-1\n");
}
else {
int cn = 0;
for(int i = 1;i <= m;i ++) {
if(ad[i]) cn ++;
}
printf("%d\n",cn);
for(int i = 1;i <= m;i ++) {
if(ad[i]) printf("%d ",i);
}
ENDL;
}
return 0;
}
模拟赛:树和森林(lct.cpp) (树形DP,换根DP好题)的更多相关文章
- [BZOJ4379][POI2015]Modernizacja autostrady[树的直径+换根dp]
题意 给定一棵 \(n\) 个节点的树,可以断掉一条边再连接任意两个点,询问新构成的树的直径的最小和最大值. \(n\leq 5\times 10^5\) . 分析 记断掉一条边之后两棵树的直径为 \ ...
- 换根DP+树的直径【洛谷P3761】 [TJOI2017]城市
P3761 [TJOI2017]城市 题目描述 从加里敦大学城市规划专业毕业的小明来到了一个地区城市规划局工作.这个地区一共有ri座城市,<-1条高速公路,保证了任意两运城市之间都可以通过高速公 ...
- 树形dp换根,求切断任意边形成的两个子树的直径——hdu6686
换根dp就是先任取一点为根,预处理出一些信息,然后在第二次dfs过程中进行状态的转移处理 本题难点在于任意割断一条边,求出剩下两棵子树的直径: 设割断的边为(u,v),设down[v]为以v为根的子树 ...
- bzoj 3743 [Coci2015]Kamp——树形dp+换根
题目:https://www.lydsy.com/JudgeOnline/problem.php?id=3743 树形dp+换根. “从根出发又回到根” 减去 “mx ” . 注意dfsx里真的要改那 ...
- 【碳硫磷模拟赛】消失的+和* (树形DP)
好久没做过这么恶心的DP题了 题面 题面很简单,有一个计算式,由+号.*号.括号和小于10的正整数组成,现在所有的+和*(由于属于违禁词而)都被-号给和谐掉了,现在要求所有可能的原计算式的结果之和. ...
- 7.18 NOI模拟赛 树论 线段树 树链剖分 树的直径的中心 SG函数 换根
LINK:树论 不愧是我认识的出题人 出的题就是牛掰 == 他好像不认识我 考试的时候 只会写42 还有两个subtask写挂了 拿了37 确实两个subtask合起来只有5分的好成绩 父亲能转移到自 ...
- [题解](树形dp/换根)小x游世界树
2. 小x游世界树 (yggdrasi.pas/c/cpp) [问题描述] 小x得到了一个(不可靠的)小道消息,传说中的神岛阿瓦隆在格陵兰海的某处,据说那里埋藏着亚瑟王的宝藏,这引起了小x的好奇,但当 ...
- poj3585 Accumulation Degree(树形dp,换根)
题意: 给你一棵n个顶点的树,有n-1条边,每一条边有一个容量z,表示x点到y点最多能通过z容量的水. 你可以任意选择一个点,然后从这个点倒水,然后水会经过一些边流到叶节点从而流出.问你最多你能倒多少 ...
- 树链剖分(附带LCA和换根)——基于dfs序的树上优化
.... 有点懒: 需要先理解几个概念: 1. LCA 2. 线段树(熟练,要不代码能调一天) 3. 图论的基本知识(dfs序的性质) 这大概就好了: 定义: 1.重儿子:一个点所连点树size最大的 ...
随机推荐
- .NET C#基础(7):接口 - 人如何和猫互动
0. 文章目的 面向有一定基础的C#初学者,介绍C#中接口的意义.使用以及特点. 1. 阅读基础 了解C#基本语法(如定义一个类.继承一个类) 理解OOP中的基本概念(如继承,多态) 2. ...
- MAUI与Blazor共享一套UI,媲美Flutter,实现Windows、macOS、Android、iOS、Web通用UI
1. 前言 距离上次发<MAUI初体验:爽>一文已经过去2个月了,本计划是下半年或者明年再研究MAUI的,现在计划提前啦,因为我觉得MAUI Blazor挺有意思的:在Android.iO ...
- 20天等待,申请终于通过,安装和体验IntelliJ IDEA新UI预览版
欢迎访问我的GitHub 这里分类和汇总了欣宸的全部原创(含配套源码):https://github.com/zq2599/blog_demos 关于IDEA的预览版 IDEA会启用新的UI,这事情之 ...
- 图片管够!用Python做了个图片识别系统(附源码)
本项目将使用python3去识别图片是否为色情图片,会使用到PIL这个图像处理库,并且编写算法来划分图像的皮肤区域 介绍一下PIL: PIL(Python Image Library)是一种免费的图像 ...
- 两分钟解决Python读取matlab的.mat数据
Matlab是学术界非常受欢迎的科学计算平台,matlab提供强大的数据计算以及仿真功能.在Matlab中数据集通常保存为.mat格式.那么如果我们想要在Python中加载.mat数据应该怎么办呢?所 ...
- Metasploit(msf)利用ms17_010(永恒之蓝)出现Encoding::UndefinedConversionError问题
Metasploit利用ms17_010(永恒之蓝) 利用流程 先确保目标靶机和kali处于同一网段,可以互相Ping通 目标靶机防火墙关闭,开启了445端口 输入search ms17_010 搜索 ...
- RS485通信电路
RS485由RS232和RS422发展而来,弥补了抗干扰能力差.通信距离短.速率低的缺点,增加了多点.双向通信能力,即允许多个发送器连接在同一条主线上,同时增加了发送器的驱动能力和冲突保护特性,扩展了 ...
- ReentrantLock 公平锁源码 第2篇
Reentrant 2 前两篇写完了后我自己研究了下,还有有很多疑惑和问题,这篇就继续以自问自答的方式写 如果没看过第1篇的可以先看看那个https://www.cnblogs.com/sunanka ...
- sudoer文件配置错误修复
以错误配置权限为例,如果是sudoer文件内容配置错误,替换步骤(4)中相关命令即可 (1)建立两个ssh连接,分别记为A.B (2)A:echo $$获取ID (3)B:pkttyagent --p ...
- 抢先体验! 在浏览器里写 Flutter 是一种什么体验?
Invertase 是一间位于英国的开源软件制作公司.主要构建关于开发者工具.SDK 等应用程序,早在 Flutter 2.2 的时候,Invertase 团队就开始帮助构建和贡献 Firebase ...