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;
};
vectoredge[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<