【基環樹】P1453 城市環路 &&P2607 騎士

1.P1453城市環路

題目背景

一座城市,每每會被人們劃分爲幾個區域,例如住宅區、商業區、工業區等等。B市就被分爲了如下的兩個區域——城市中心和城市郊區。在着這兩個區域的中間是一條圍繞B市的環路,環路以內即是B市中心。ios

題目描述

整個城市能夠看作一個N個點,N條邊的單圈圖(保證圖連通),惟一的環即是繞城的環路。保證環上任意兩點有且只有2條路徑互通。圖中的其它部分皆隸屬城市郊區。數組

如今,有一位名叫Jim的同窗想在B市開店,可是任意一條邊的2個點不能同時開店,每一個點都有必定的人流量Pi,在該點開店的利潤就等於該店的人流量Pi×K(K≤10000),K的值將給出。ide

Jim想盡可能多的賺取利潤,請問他應該在哪些地方開店?函數

輸入格式

第一行一個整數N 表明城市中點的個數。城市中的N個點由0~N-1編號。測試

第二行N個正整數,表示每一個點的人流量Pi(Pi≤10000)。ui

下面N行,每行2個整數A,B,表示A,B建有一條雙向路。spa

最後一行一個實數K。code

輸出格式

一個實數M,(保留1位小數),表明開店的最大利潤。orm

輸入輸出樣例

輸入 #1
4
1 2 1 5
0 1
0 2
1 2
1 3
2
輸出 #1
12.0

說明/提示

【數據範圍】blog

對於20%的數據,N≤100.

對於另外20%的數據,環上的點不超過2000個

對於50%的數據 N≤50000.

對於100%的數據 N≤100000.


前置知識

  基環樹

    咱們都知道,出題人都以出出有意義的題目爲己任【其實就是毒瘤

    當咱們能夠熟練掌握圖上和樹上的任務的時候,出題人不爽了。

    某一天,某位毒瘤出題人在深夜被汽車吵醒,以後終於發現一個神奇的東西。

      集樹形結構和環路於一體,又不至於不可作的東西。

    基環樹!!!

    【上面都是我本身yy的QAQ

    無論過程到底是什麼樣子的了,總之,咱們如今有可能考這個奇怪的東西。

    究竟什麼是基環樹呢?

  私自定義1:

    在一棵樹上面,加一條邊,造成一顆只含有一個環的樹【霧

    

    好比這張圖。【對不起我又盜圖了QAQ

  私自定義2:

    一張只有一個環的圖。

    既然能夠當作一張圖,咱們都知道圖是能夠有向的。

    那麼基環樹也是能夠有向的。

    

    指向環的:內向基環樹

    

    起源於環的:外向基環樹。

    那麼怎麼才能解決這種問題呢?一會在題面中解釋吧。


 

本題思路分析

  咱們從題面中有這樣一句話:

  N條邊的單圈圖(保證圖連通),惟一的環即是繞城的環路。

  這說明了什麼?

  構想一下地圖的場景,假設只有一個環,那麼這張圖就是一顆基環樹。

  啊啊啊,好但願這能是一顆樹啊?

  這樣就能採起樹形DP了啊QAQ

  emmm,好像是有辦法的,指變成樹。

  咱們仔細想一想,這是一顆在樹上加了一條邊造成的結構。

  若是想從新變成一顆樹,那麼直接刪邊不就行了嘛?

  那麼究竟刪哪條邊才合適呢?

  刪環上的邊是無疑的,畢竟要保證圖的的連通性。

  可是,環上的邊有那麼多條,難道要所有都刪一遍嗎?

  固然不可能啊?怎麼證實呢?

  實際上,咱們考慮樹形DP自己的一些性質。

  咱們定義 f [ i ] 數組來記錄以 i 爲根的子樹的性質。

  而後,咱們能夠任意選擇一條邊,分別從這條邊的左右端點各跑一次樹形DP。

  這樣咱們能夠獲得兩個數組,f [ ],f[ ]

  可是因爲自己定義是重合的,因此,這個數組維護的信息就同時包括着:

    1. 左節點選,右節點不選

    2. 左節點不選,右節點選

    3. 左右節點都不選

  這三種狀況,恰好把不知足題意的「都選」操做給忽略了,正好是咱們所須要的。

  那麼天然,跑兩遍tree DP就OK了。


AC代碼放送

 1 #include<iostream>
 2 #include<cstdio>
 3 #include<cstring>
 4 #include<cmath>
 5 #include<algorithm>
 6 #define ll long long
 7 #define uint unsigned int
 8 using namespace std;
 9 
10 const int MA=1e6+10;
11 ll n,val[MA];
12 struct ss{
13     ll to,nxt;
14 }tr[MA*2];
15 ll head[MA],ecnt=1;
16 
17 inline void add(ll a,ll b) {
18     tr[++ecnt].nxt=head[a];
19     tr[ecnt].to=b;
20 
21     head[a]=ecnt;
22     return;
23 }
24 
25 ll sta,ed,bian;
26 
27 
28 bool vis[MA];
29 
30 void dfs(ll x,ll fa) {
31     vis[x]=1;
32     for(uint i=head[x];i;i=tr[i].nxt) {
33         ll y=tr[i].to;
34         if(y==fa)
35             continue;
36         if(vis[y])  {
37             sta=x;
38             ed=y;
39             bian=i;
40 
41 
42             continue;
43         }
44         dfs(y,x);
45     }
46     return;
47 }
48 
49 double f[MA][2];
50 
51 void dp(ll x,ll fa) {
52     f[x][0]=0;
53     f[x][1]=val[x];
54     for(uint i=head[x];i;i=tr[i].nxt) {
55         ll y=tr[i].to;
56         if(i==bian||i==(bian^1))
57 
58             continue;
59         if(y==fa) 
60             continue;
61         dp(y,x);
62         f[x][0]+=max(f[y][0],f[y][1]);
63         f[x][1]+=f[y][0];
64     }
65     return;
66 }
67 
68 int main()
69 {
70     scanf("%lld",&n);
71 
72     for(uint i=1;i<=n;i++) 
73         scanf("%lld",&val[i]);    
74     for(uint i=1;i<=n;i++) {
75         ll x,y;
76         scanf("%lld%lld",&y,&x);
77 
78         x++,y++;
79         add(y,x);
80 
81         add(x,y);
82 
83     }
84     double k;
85     scanf("%lf",&k);
86 
87     double ans=0;
88     dfs(1,1);
89     dp(sta,sta);
90     double tmp=f[sta][0];
91     dp(ed,ed);
92     ans+=max(tmp,f[ed][0]);
93     printf("%.1lf\n",ans*k);
94     return 0;
95 }
城市環路

 


 

2.騎士

題目描述

Z國的騎士團是一個頗有勢力的組織,幫會中匯聚了來自各地的精英。他們劫富濟貧,懲惡揚善,受到社會各界的讚賞。

最近發生了一件可怕的事情,邪惡的Y國發動了一場針對Z國的侵略戰爭。戰火綿延五百里,在和平環境中安逸了數百年的Z國又怎能抵擋的住Y國的軍隊。因而人們把全部的但願都寄託在了騎士團的身上,就像期待有一個真龍天子的降生,帶領正義戰勝邪惡。

騎士團是確定具備戰勝邪惡勢力的能力的,可是騎士們互相之間每每有一些矛盾。每一個騎士都有且僅有一個本身最厭惡的騎士(固然不是他本身),他是絕對不會與本身最厭惡的人一同出征的。

戰火綿延,人民生靈塗炭,組織起一個騎士軍團加入戰鬥刻不容緩!國王交給了你一個艱鉅的任務,從全部的騎士中選出一個騎士軍團,使得軍團內沒有矛盾的兩人(不存在一個騎士與他最痛恨的人一同被選入騎士軍團的狀況),而且,使得這支騎士軍團最具備戰鬥力。

爲了描述戰鬥力,咱們將騎士按照1至N編號,給每名騎士一個戰鬥力的估計,一個軍團的戰鬥力爲全部騎士的戰鬥力總和。

輸入格式

輸入文件knight.in第一行包含一個正整數N,描述騎士團的人數。

接下來N行,每行兩個正整數,按順序描述每一名騎士的戰鬥力和他最痛恨的騎士。

輸出格式

輸出文件knight.out應包含一行,包含一個整數,表示你所選出的騎士軍團的戰鬥力。

輸入輸出樣例

輸入 #1
3
10 2
20 3
30 1
輸出 #1
30

說明/提示

對於30%的測試數據,知足N ≤ 10;

對於60%的測試數據,知足N ≤ 100;

對於80%的測試數據,知足N ≤ 10 000。

對於100%的測試數據,知足N ≤ 1 000 000,每名騎士的戰鬥力都是不大於 1 000 000的正整數


 

思路 分析

  經過觀察題面,咱們發現,這張圖上面頗有可能不止一個環,甚至可能不是聯通的。

  可是,能夠感性理解一下,對於每個聯通塊,有且僅有一個環。

  爲何呢?

  由於,每一個節點都要有一條出邊【我恨他,和不少條入邊【你們都恨我

  那麼有資格向下發出一條出邊的節點也就是根節點了。

   如此一來,這就是一片基環樹森林。

  既然有了基環樹這麼一個優秀的性質,對於森林只須要保證所有的樹都被遍歷到就行了。

  最後還有一點,本題的圖不是有向圖,恨的關係看起來是單向的,但本質上是雙向的。

  好比說A恨B,而後B知道A恨他,因而也不想跟A一塊走。【由愛生恨?


AC代碼放送

 1 #include<iostream>
 2 #include<cstdio>
 3 #include<cstring>
 4 #include<cmath>
 5 #include<algorithm>
 6 #define ll long long
 7 #define uint unsigned int
 8 using namespace std;
 9 
10 const int MA=1e6+10;
11 ll n,val[MA];
12 struct ss{
13     ll to,nxt;
14 }tr[MA*2];
15 ll head[MA],ecnt=1;
16 
17 inline void add(ll a,ll b) {
18     tr[++ecnt].nxt=head[a];
19     tr[ecnt].to=b;
20 
21     head[a]=ecnt;
22     return;
23 }
24 
25 ll sta,ed,bian;
26 
27 bool vis[MA];
28 
29 void dfs(ll x,ll fa) {
30     vis[x]=1;
31     for(uint i=head[x];i;i=tr[i].nxt) {
32         ll y=tr[i].to;
33         if(y==fa)
34             continue;
35         if(vis[y])  {
36             sta=x;
37             ed=y;
38             bian=i;
39 
40             continue;
41         }
42         dfs(y,x);
43     }
44     return;
45 }
46 
47 ll f[MA][2];
48 
49 void dp(ll x,ll fa) {
50     f[x][0]=0;
51     f[x][1]=val[x];
52     for(uint i=head[x];i;i=tr[i].nxt) {
53         ll y=tr[i].to;
54         if(i==bian||i==(bian^1))
55 
56             continue;
57         if(y==fa) 
58             continue;
59         dp(y,x);
60         f[x][0]+=max(f[y][0],f[y][1]);
61         f[x][1]+=f[y][0];
62     }
63     return;
64 }
65 
66 int main()
67 {
68     scanf("%lld",&n);
69 
70     for(uint i=1;i<=n;i++) {
71         ll x;
72         scanf("%lld%lld",&val[i],&x);
73 
74         add(i,x);
75 
76         add(x,i);
77 
78     }
79     ll ans=0;
80     for(uint i=1;i<=n;i++) {
81         if(vis[i])
82             continue;
83         dfs(i,i);
84         dp(sta,sta);
85         ll tmp=f[sta][0];
86         dp(ed,ed);
87         ans+=max(tmp,f[ed][0]);
88     }
89     printf("%lld\n",ans);
90     return 0;
91 }
騎士

最後的建議

  提交代碼以前必定要看好dfs函數,有沒有出現無線遞歸的狀況,否則會MLE。QAQ

祝你們AC愉快!

相關文章
相關標籤/搜索