RP路
题目描述
小X对RP十分有研究,现在他向你提出了一个问题。给出一棵N个节点的树,有些边是RP边,至少经过一条RP边的路径定义为RP路,其他路径定义为非RP路。现在,你需要令最多一条RP边变为非RP边,使得RP路的数量最少。
数据范围
测试点 N
1~2 ≤100
3~5 ≤5 000
6 ≤10 000
7~10 ≤100 000
输入格式 2074.in
第一行一个正整数N。
接下来N-1行,每行三个整数A,B,C。表示节点A,B间存在一条边,如果是RP边,那么C=1,否则C=0。节点从1开始到N标号。
输出格式 2074.out
一个表示你的答案的整数。
输入样例 2074.in
5
3 5 1
1 4 1
5 2 1
2 1 0
输出样例 2074.out
7
这题,涉及到一种转化思想。基本思路如下:我们直接求RP路径有多少条,不太好求,直接求是N*N的。那就将其转化成总路径条数减去非RP路径条数,这就是初始RP路径条数。然后,分别计算,去掉每条RP边,会减少的RP路径条数。初始RP路径条数减去这其中的最大值即为所求。
总路径条数?自然是N*(N-1)/2。
那么,非RP路径如何求?根据定义,非RP路径中的所有边皆为非RP边,其中不能有RP边。通过手动画RP树可以发现,可以将原图进行“缩点”,缩成以RP路为树边,以非RP子集为结点的树。(缩点可用并查集/DFS)。所有新点内部的路径数之和即为非RP路径总数。
去掉每条RP边,会减少的RP路径条数如何求?明确一点:会被减少的RP路径,路径中有且只有一条RP边,就是被删的那条。因此不难发现,会减少的RP路径条数,等于此RP边所连接的两非RP子集的结点乘积。
至此,题目所求可以求出。
说白了,此题我没有想出的原因,就是没有想到这样“缩点”。因此,观察特征,总结潜在规律,打开思路解题很重要。。。。造化不够。。。。。……
代码如下:
#include
#include
#include
using namespacestd;
const intMAXN=1e5+5;
struct poi
{
int to,type;
};
vector
edge[MAXN];
long longn,a,b,c,f[MAXN],v[MAXN],father[MAXN],vi[MAXN];
long longans,minu;
int findA(int x)
{
if(father[x]==x) return x;
return father[x]=findA(father[x]);
}
void init(inta,int b,int c)
{
edge[a].push_back((poi){b,c});
edge[b].push_back((poi){a,c});
if(c==0)
{
int root1=findA(a),root2=findA(b);
father[root1]=root2;
}
}
void dfs(intnow)
{
vi[now]=1;
int size=edge[now].size();
for(int i=0;i
>n; for(int i=1;i<=n;i++) father[i]=i; for(int i=1;i
>a>>b>>c; init(a,b,c); } for(int i=1;i<=n;i++) { int root=findA(i); father[i]=root; v[father[i]]++; } ans=n*(n-1)/2; for(int i=1;i<=n;i++) ans-=v[i]*(v[i]-1)/2; minu=0; dfs(1); cout<
<