岛屿(bzoj1791)
Posted lmjer
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了岛屿(bzoj1791)相关的知识,希望对你有一定的参考价值。
1791: [Ioi2008]Island 岛屿
Time Limit: 20 Sec Memory Limit: 162 MBSubmit: 2042 Solved: 461
[Submit][Status][Discuss]
Description
你将要游览一个有N个岛屿的公园。从每一个岛i出发,只建造一座桥。桥的长度以Li表示。公园内总共有N座桥。尽管每座桥由一个岛连到另一个岛,但每座桥均可以双向行走。同时,每一对这样的岛屿,都有一艘专用的往来两岛之间的渡船。
相对于乘船而言,你更喜欢步行。你希望所经过的桥的总长度尽可能的长,但受到以下的限制。
? 可以自行挑选一个岛开始游览。
? 任何一个岛都不能游览一次以上。
? 无论任何时间你都可以由你现在所在的岛S去另一个你从未到过的岛D。由S到D可以有以下方法:
o 步行:仅当两个岛之间有一座桥时才有可能。对于这种情况,桥的长度会累加到你步行的总距离;或者
o 渡船:你可以选择这种方法,仅当没有任何桥和/或以前使用过的渡船的组合可以由S走到D(当检查是否可到达时,你应该考虑所有的路径,包括经过你曾游览过的那些岛)。
注意,你不必游览所有的岛,也可能无法走完所有的桥。
任务
编写一个程序,给定N座桥以及它们的长度,按照上述的规则,计算你可以走过的桥的最大长度。
限制
2 <= N <= 1,000,000 公园内的岛屿数目。
1<= Li <= 100,000,000 桥i的长度。
Input
? 第一行包含N个整数,即公园内岛屿的数目。岛屿由1到N编号。
? 随后的N行每一行用来表示一个岛。第i 行由两个以单空格分隔的整数,表示由岛i筑的桥。第一个整数表示桥另一端的岛,第二个整数表示该桥的长度Li。你可以假设对於每座桥,其端点总是位于不同的岛上。
Output
你的程序必须向标准输出写出包含一个整数的单一行,即可能的最大步行距离。
注1:对某些测试,答案可能无法放进32-bit整数,你要取得这道题的满分,可能需要用Pascal的int64或C/C++的long long类型。
注2:在比赛环境运行Pascal程序,由标准输入读入64-bit数据比32-bit数据要慢得多,即使被读取的数据可以32-bit表示。我们建议把输入数据读入到32-bit数据类型。
评分
N不会超过4,000。
Sample Input
7
3 8
7 2
4 2
1 4
1 9
3 4
2 3
3 8
7 2
4 2
1 4
1 9
3 4
2 3
Sample Output
24
HINT
这道题可以学到很多东西
首先先以环中每个点为根求出最远能到达的距离
然后最长链为环中某两点的距离再加上,这两点最远到达的距离
然后具体怎么求这些东西呢
首先求以环中每个点为根求出最远能到达的距离
我们先标记每个点的入度,若一开始入度为一,则说明此点为叶子节点,然后通过不断向上求,并且不断删掉该点的叶子节点个数,就可以求到环上的点
而最后环上的点必定入度为二,所以可以实现避免环上的点求最远距离,并且可以找出环来
void topsort(){
ll l=1,r=0;
for (ll i=1;i<=n;i++) if(du[i]==1) Q[++r]=i;
while(l<=r){
ll u=Q[l];
for (ll i=adj[u];i;i=bian[i].next){
ll v=bian[i].v;
if(du[v]>1){
d[c[u]]=max(d[c[u]],f[v]+f[u]+bian[i].w);
f[v]=max(f[v],f[u]+bian[i].w);
if((--du[v])==1) Q[++r]=v;
}
}
l++;
}
}
最后求环上的最远距离可以把环拆开复制成两倍做成一条链,便可以用单调队列方法求最值
void getans(ll t,ll x){
ll y=x;
ll i,r,l=0;
ll m=0;
do{
a[++m]=f[y];
du[y]=1;
for (i=adj[y];i;i=bian[i].next){
ll v=bian[i].v;
if(du[v]>1){
b[m+1]=b[m]+bian[i].w;
y=v;
break;
}
}
}while(i);
if(m==2){void getans(ll t,ll x){
ll y=x;
ll i,r,l=0;
ll m=0;
do{
a[++m]=f[y];
du[y]=1;
for (i=adj[y];i;i=bian[i].next){
ll v=bian[i].v;
if(du[v]>1){
b[m+1]=b[m]+bian[i].w;
y=v;
break;
}
for (i=adj[y];i;i=bian[i].next){
if(bian[i].v==x) {b[2]=max(b[2],bian[i].w);}
}
d[t]=max(d[t],f[x]+f[y]+b[2]);
return ;
}
for (i=adj[y];i;i=bian[i].next){
if(bian[i].v==x){
b[m+1]=b[m]+bian[i].w;
break;
}
}
for (i=1;i<m;i++) {a[m+i]=a[i];b[m+i]=b[m+1]+b[i];}
Q[l=r=1]=1;
for (i=2;i<2*m;i++){
while(l<=r&&i-Q[l]>=m) l++;
d[t]=max(d[t],a[i]+a[Q[l]]+b[i]-b[Q[l]]);
while(l<=r&&a[Q[r]]+b[i]-b[Q[r]]<=a[i]) r--;
Q[++r]=i;
}
}
#include<cstdio> #include<algorithm> #include<cstring> using namespace std; const int maxn=1000000+10; typedef long long ll; struct my{ int v; int next; ll w; }; my bian[maxn*2]; int adj[maxn],n,c[maxn],du[maxn],Q[maxn*2]; bool vis[maxn]; ll f[maxn],fa,d[maxn],a[maxn*2],b[maxn*2]; void myinsert(ll u,ll v,ll w){ bian[++fa].v=v; bian[fa].next=adj[u]; bian[fa].w=w; adj[u]=fa; du[v]++; } void dfs(ll x,ll t){ c[x]=t; for (ll i=adj[x];i;i=bian[i].next){ ll v=bian[i].v; if(!c[v]){ dfs(v,t); } } } void topsort(){ ll l=1,r=0; for (ll i=1;i<=n;i++) if(du[i]==1) Q[++r]=i; while(l<=r){ ll u=Q[l]; for (ll i=adj[u];i;i=bian[i].next){ ll v=bian[i].v; if(du[v]>1){ d[c[u]]=max(d[c[u]],f[v]+f[u]+bian[i].w); f[v]=max(f[v],f[u]+bian[i].w); if((--du[v])==1) Q[++r]=v; } } l++; } } void getans(ll t,ll x){ ll y=x; ll i,r,l=0; ll m=0; do{ a[++m]=f[y]; du[y]=1; for (i=adj[y];i;i=bian[i].next){ ll v=bian[i].v; if(du[v]>1){ b[m+1]=b[m]+bian[i].w; y=v; break; } }//把环拆成链 }while(i); if(m==2){ for (i=adj[y];i;i=bian[i].next){ if(bian[i].v==x) {b[2]=max(b[2],bian[i].w);} } d[t]=max(d[t],f[x]+f[y]+b[2]); return ; } for (i=adj[y];i;i=bian[i].next){ if(bian[i].v==x){ b[m+1]=b[m]+bian[i].w; break;把环的开头找到、、、、、、、、、、、、、、、、、、、、、、、、、、、、、、、、、、、、、、、、、、 } } for (i=1;i<m;i++) {a[m+i]=a[i];b[m+i]=b[m+1]+b[i];} Q[l=r=1]=1; for (i=2;i<2*m;i++){ while(l<=r&&i-Q[l]>=m) l++; d[t]=max(d[t],a[i]+a[Q[l]]+b[i]-b[Q[l]]); while(l<=r&&a[Q[r]]+b[i]-b[Q[r]]<=a[i]) r--; Q[++r]=i; } } int main(){ // freopen("bzoj1791.in","r",stdin); //freopen("bzoj1791.out","w",stdout); scanf("%lld",&n); ll u,w; for (int i=1;i<=n;i++){ scanf("%lld%lld",&u,&w); myinsert(u,i,w); myinsert(i,u,w); } ll t=0; for (ll i=1;i<=n;i++) if(!c[i]) { ++t; dfs(i,t);//标记每棵树 } topsort(); ll ans=0; for(ll i=1;i<=n;i++){ if(du[i]>1&&!vis[c[i]]){ vis[c[i]]=1; getans(c[i],i); ans+=d[c[i]]; } } printf("%lld ",ans); return 0; }
以上是关于岛屿(bzoj1791)的主要内容,如果未能解决你的问题,请参考以下文章
[bzoj1791][ioi2008]Island 岛屿(基环树树的直径)
bzoj 1791: [Ioi2008]Island 岛屿基环树+单调队列优化dp