4871: [Shoi2017]摧毁“树状图”
Time Limit: 25 Sec Memory Limit: 512 MB
Submit: 133 Solved: 45
[Submit][Status][Discuss]
Description
自从上次神刀手帮助蚯蚓国增添了上千万人口(蚯口?),蚯蚓国发展得越来越繁荣了!最近,他们在地下发现了
一些神奇的纸张,经过仔细研究,居然是D国X市的超级计算机设计图纸!这台计算机叫做‘树状图’,由n个计算
节点与n1条可以双向通信的网线连接而成,所有计算节点用不超过n的正整数编号。顾名思义,这形成了一棵树的
结构。蚯蚓国王已在图纸上掌握了这棵树的完整信息,包括n的值与n1条网线的连接信息。于是蚯蚓国王决定,派
出蚯蚓国最强大的两个黑客,小P和小H,入侵‘‘树状图’’,尽可能地摧毁它。小P和小H精通世界上最好的编程
语言,经过一番商量后,他们决定依次采取如下的步骤:小P选择某个计算节点,作为他入侵的起始点,并在该节
点上添加一个P标记。重复以下操作若干次(可以是0次):–小P从他当前所在的计算节点出发,选择一条没有被
标记过的网线,入侵到该网线的另一端的计算节点,并在路过的网线与目的计算节点上均添加一个P标记。小H选择
某个计算节点,作为她入侵的起始点,并在该节点上添加一个H标记。重复以下操作若干次(可以是0次):–小H
从她当前所在的计算节点出发,选择一条没有被标记过的网线,入侵到该网线的另一端的计算节点,并在路过的网
线与目的计算节点上均添加一个H标记。(注意,小H不能经过带有P标记的网线,但是可以经过带有P标记的计算节
点)删除所有被标记过的计算节点和网线。对于剩下的每条网线,如果其一端或两端的计算节点在上一步被删除了
,则也删除这条网线。经过以上操作后,‘‘树状图’’会被断开,剩下若干个(可能是0个)连通块。为了达到
摧毁的目的,蚯蚓国王希望,连通块的个数越多越好。于是他找到了你,希望你能帮他计算这个最多的个数。小P
和小H非常心急,在你计算方案之前,他们可能就已经算好了最优方案或最优方案的一部分。你能得到一个值x:若
x=0,则说明小P和小H没有算好最优方案,你需要确定他们两个的入侵路线。若x=1,则说明小P已经算好了某种两
人合作的最优方案中,他的入侵路线。他将选择初始点p0,并沿着网线一路入侵到了目标点p1,并且他不会再沿着
网线入侵;你只需要确定小H的入侵路线。若x=2,则说明小P和小H算好了一种两人合作的最优方案,小P从点p0入
侵到了p1并停下,小H从点h0入侵到了h1并停下。此时你不需要指挥他们入侵了,只需要计算最后两步删除计算节
点与网线后,剩下的连通块个数即可。
Input
每个输入文件包含多个输入数据。输入文件的第一行为两个整数 T 和 x, T 表示
该文件包含的输入数据个数, x 的含义见上述。(同一个输入文件的所有数据的 x 都是相同的)
接下来依次输入每个数据。
每个数据的第一行有若干个整数:
若 x = 0,则该行只有一个整数 n。
若 x = 1,则该行依次有三个整数 n, p0, p1。
若 x = 2,则该行依次有五个整数 n, p0, p1, h0, h1。
保证 p0, p1, h0, h1 均为不超过 n 的正整数。
每个数据接下来有 n 1 行,每行有两个不超过 n 的正整数,表示这两个编号的计
算节点之间有一条网线将其相连。保证输入的是一棵树。
同一行相邻的整数之间用恰好一个空格隔开。
对于整数 k,设 ∑ n^k 为某个输入文件中,其 T 个输入数据的 n^k 之和。
所有输入文件满足 T ≤ 10^5, ∑ n^1 ≤ 5 × 10^5。
数据文件可能较大,请避免使用过慢的输入输出方法。
Output
对于每个数据,输出一行,表示在给定条件下,剩下连通块的最大个数。
Sample Input
1 0
13
1 2
2 3
2 4
4 5
4 6
4 7
7 8
7 9
9 10
10 11
10 12
12 13
Sample Output
8
这个输入文件只有一个输入数据。一种最优的方案如下:
小 P 从节点 2 开始入侵,节点 2 被小 P 标记。
小 P 从节点 2 入侵到节点 4,节点 4 和经过的网线被小 P 标记。
小 P 从节点 4 入侵到节点 7,节点 7 和经过的网线被小 P 标记。
小 H 从节点 10 开始入侵,节点 10 被小 H 标记。
删除被标记的节点 2,4,7,10 和被标记的网线 (2,4) 和 (4,7)。
删除任意一端在上一步被删除的网线。
此时还剩下 8 个连通块。其中节点 1,3,5,6,8,9,11 各自形成一个连通块,节点 12,13
形成了一个连通块。
HINT
2017.4.28新加数据一组By 150137
Source
黑吉辽沪冀晋六省联考
[Submit][Status][Discuss]
考虑所有答案可能的情况
若两路径不相交:
1. 两路径顶点的 LCA 不是两路径的顶点
2. 两路径顶点的 LCA 是其中一条的顶点
若两路径相交,则交集肯定是一个单独的点
枚举交集延伸出去的链的条数,必定不超过四
然后对于每个节点,维护一下几种图形
0:经过当前根的一条链
1:子树中的一条路径
2:经过当前根的一条路径
3:过当前根类似”三叉戟”的图型
4:过当前根的一条链已经子树总的一条路径,不相交
显然各种答案是可以通过它们拼出来的
转移嘛。。。。。。。手动推吧
真的一点都不麻烦!!!
#include
#include
#include
#include<vector>
#define max(a,b) ((a) > (b) ? (a) : (b))
using namespace std;
const int maxn = 5E5 + 5;
int T,G,GG,n,Ans,I,f[maxn][5];
vector <int> v[maxn];
inline int getint()
{
char ch = getchar(); int ret = 0;
while (ch < '0' || '9' < ch) ch = getchar();
while ('0' <= ch && ch <= '9')
ret = ret * 10 + ch - '0',ch = getchar();
return ret;
}
inline void Dfs(int x,int from)
{
int fg = x == 1 ? 0 : 1,sz = v[x].size();
int fr,sc,ti,fu,m1,m2,mx,pf,ps;
fr = sc = ti = fu = m1 = m2 = mx = pf = ps = 0;
for (int i = 0; i < sz; i++)
{
int to = v[x][i];
if (to == from) continue; Dfs(to,x);
f[x][0] = max(f[x][0],f[to][0] + sz - fg - 1);
f[x][1] = max(f[x][1],max(f[to][2],f[to][1]));
f[x][3] = max(f[x][3],f[to][3] + sz - fg - 1);
f[x][4] = max(f[x][4],f[to][4] + sz - fg - 1);
f[x][4] = max(f[x][4],f[to][1] + sz - fg);
f[x][4] = max(f[x][4],f[to][1] + sz - fg - 1 + fr);
f[x][4] = max(f[x][4],f[to][2] + sz - fg - 1);
f[x][4] = max(f[x][4],f[to][2] + sz + fr - fg - 2);
f[x][4] = max(f[x][4],f[to][0] + mx + sz - fg - 1);
if (f[to][0] >= fr)
{
fu = ti; ti = sc; sc = fr;
ps = pf; fr = f[to][0]; pf = to;
}
else if (f[to][0] >= sc)
fu = ti,ti = sc,sc = f[to][0],ps = to;
else if (f[to][0] >= ti) fu = ti,ti = f[to][0];
else fu = max(fu,f[to][0]);
int tmp = max(f[to][1],f[to][2]);
if (tmp >= m1) m2 = m1,m1 = tmp;
else m2 = max(m2,tmp);
mx = max(mx,max(f[to][1],f[to][2] - 1));
}
f[x][0] = max(f[x][0],sz - fg);
Ans = max(Ans,m1 + m2 + 1); f[x][2] = f[x][0];
f[x][2] = max(f[x][2],fr + sc + sz - fg - 2);
f[x][3] = max(f[x][3],max(f[x][0],f[x][2]));
f[x][3] = max(f[x][3],fr + sc + ti + sz - fg - 3);
Ans = max(Ans,f[x][0] + fg);
Ans = max(Ans,f[x][2] + fg);
Ans = max(Ans,f[x][3] + fg);
Ans = max(Ans,fr + sc + ti + fu + sz - 4);
for (int i = 0; i < sz; i++)
{
int to = v[x][i],One,Two;
if (to == from) continue;
if (to == pf) One = sc,Two = sc + ti;
else if (to == ps) One = fr,Two = fr + ti;
else One = fr,Two = fr + sc;
Ans = max(Ans,f[to][1] + sz);
Ans = max(Ans,f[to][1] + One + sz - 1);
Ans = max(Ans,f[to][1] + Two + sz - 2);
Ans = max(Ans,f[to][2] + One + sz - 2);
Ans = max(Ans,f[to][2] + Two + sz - 3);
Ans = max(Ans,f[to][3] + sz - 1);
Ans = max(Ans,f[to][3] + One + sz - 2);
Ans = max(Ans,f[to][4] + sz - 1);
Ans = max(Ans,f[to][4] + One + sz - 2);
}
}
inline void Clear()
{
Ans = 0;
for (int i = 1; i <= n; i++)
memset(f[i],0,sizeof(f[i])),v[i].clear();
}
inline void Solve()
{
n = getint();
for (int i = 0; i < G; i++) GG = getint();
for (int i = 1; i < n; i++)
{
int x = getint(),y = getint();
v[x].push_back(y); v[y].push_back(x);
}
if (n == 1) {puts("0"); return;}
Dfs(1,0); printf("%d\n",Ans);
}
int main()
{
#ifdef DMC
freopen("DMC.txt","r",stdin);
freopen("test.txt","w",stdout);
#endif
T = getint(); G = getint() << 1;
while (T--) Solve(),Clear();
//for (I = 1; I <= 5; I++) Solve(),Clear();
//for (I = 6; I <= T; I++) Solve(),Clear();
return 0;
}