【基环树】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愉快!

相关文章
相关标签/搜索