一座城市,每每会被人们划分为几个区域,例如住宅区、商业区、工业区等等。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
4 1 2 1 5 0 1 0 2 1 2 1 3 2
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了。
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 }
Z国的骑士团是一个颇有势力的组织,帮会中汇聚了来自各地的精英。他们劫富济贫,惩恶扬善,受到社会各界的赞赏。
最近发生了一件可怕的事情,邪恶的Y国发动了一场针对Z国的侵略战争。战火绵延五百里,在和平环境中安逸了数百年的Z国又怎能抵挡的住Y国的军队。因而人们把全部的但愿都寄托在了骑士团的身上,就像期待有一个真龙天子的降生,带领正义战胜邪恶。
骑士团是确定具备战胜邪恶势力的能力的,可是骑士们互相之间每每有一些矛盾。每一个骑士都有且仅有一个本身最厌恶的骑士(固然不是他本身),他是绝对不会与本身最厌恶的人一同出征的。
战火绵延,人民生灵涂炭,组织起一个骑士军团加入战斗刻不容缓!国王交给了你一个艰巨的任务,从全部的骑士中选出一个骑士军团,使得军团内没有矛盾的两人(不存在一个骑士与他最痛恨的人一同被选入骑士军团的状况),而且,使得这支骑士军团最具备战斗力。
为了描述战斗力,咱们将骑士按照1至N编号,给每名骑士一个战斗力的估计,一个军团的战斗力为全部骑士的战斗力总和。
输入文件knight.in第一行包含一个正整数N,描述骑士团的人数。
接下来N行,每行两个正整数,按顺序描述每一名骑士的战斗力和他最痛恨的骑士。
输出文件knight.out应包含一行,包含一个整数,表示你所选出的骑士军团的战斗力。
3 10 2 20 3 30 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一块走。【由爱生恨?
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愉快!