「IOI2008」Island

他出题人要是敢砍掉一条边然后保证连通,我就敢做!

传送门

洛谷P4381

BZOJ1791

题解

题意:给定一个基环树森林,求直径和。

首先,随便拎一棵基环树出来,然后找到环。

考虑在环上DP。

先对于环上每一个节点的子树,来一遍DFS,求出从根到最深节点的距离以及子树直径。

按照环上DP的套路,先破环为链,然后复制一遍。

然后就可以DP了,但是直接暴力DP是$O(n^2)$的,所以开个单调队列优化一下即可。

恶心的是这题还卡常,自带大常数的蒟蒻我卡了好久才过。

每棵基环树的直径最后要和每个子树的直径取个max。

对于每一棵基环树分别处理即可。

代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
#include<cstdio>
#include<cstring>
typedef long long LL;
using namespace std;
const int maxn=1000005;
int n,tot=1,lnk[maxn],son[maxn*2],w[maxn*2],nxt[maxn*2],deg[maxn],C[maxn],rt,que[maxn*2];LL dep[maxn*2],sum,ans,S[maxn*2],D[maxn];bool vis[maxn],cir[maxn];
inline char nc()
{
static const int S=1048576;static char buf[S],*L,*R;
return L==R&&(R=(L=buf)+fread(buf,1,S,stdin),L==R)?EOF:*L++;
}
inline int read()
{
int ret=0,f=1;char ch=nc();
while(ch<'0'||ch>'9'){if(ch=='-')f=-f;ch=nc();}
while(ch>='0'&&ch<='9'){ret=ret*10+ch-'0';ch=nc();}
return ret*f;
}
inline void add_e(int x,int y,int z){tot++;son[tot]=y;w[tot]=z;nxt[tot]=lnk[x];lnk[x]=tot;}
inline void GetTree(int x)
{
int hed=0,til=1;
que[1]=x;vis[x]=true;
while(hed!=til)
{
hed++;C[++C[0]]=que[hed];
for(int i=lnk[que[hed]];i;i=nxt[i])
{
if(!vis[son[i]])
{
vis[son[i]]=true;
que[++til]=son[i];
}
}
}
}
void Toposort()
{
int hed=0,til=0;
for(int i=1;i<=C[0];i++)
if(deg[C[i]]==1)
que[++til]=C[i];
while(hed!=til)
{
hed++;cir[que[hed]]=false;
for(int i=lnk[que[hed]];i;i=nxt[i])
{
if(deg[son[i]])
{
if(D[son[i]]<D[que[hed]]+w[i]) D[son[i]]=D[que[hed]]+w[i];
deg[son[i]]--;
deg[que[hed]]--;
if(deg[son[i]]==1) que[++til]=son[i];
}
}
}
int p=0,lst=0,len=0;
for(int i=1;i<=C[0];i++) if(cir[C[i]]){p=C[i];len++;}
C[0]=len;
for(int i=1;i<=C[0];i++)
for(int j=lnk[p];j;j=nxt[j])
if((j^1)!=lst&&cir[son[j]])
{C[i]=p;lst=j;p=son[j];S[i+1]=S[i+1+C[0]]=w[j];break;}
for(int i=1;i<=C[0]*2;i++) S[i]+=S[i-1];
}
void GetD(int now,int fa,LL dist)
{
if(dist>sum){sum=dist;rt=now;}
for(int i=lnk[now];i;i=nxt[i])
if(son[i]!=fa&&!cir[son[i]])
GetD(son[i],now,dist+w[i]);
}
inline LL Solve(int x)
{
LL ret=0,tep;C[0]=0;
GetTree(x);
Toposort();
for(int i=1;i<=C[0];i++)
{
dep[i+C[0]]=dep[i]=D[C[i]];
cir[C[i]]=false;rt=C[i];
sum=0;GetD(C[i],0,0);
sum=0;GetD(rt,0,0);
cir[C[i]]=true;
if(sum>ret) ret=sum;
}
int hed=0,til=0;
for(int i=1;i<=C[0]*2;i++)
{
while(hed<til&&i-que[hed]>=C[0]) hed++;
tep=dep[i]+dep[que[hed]]+S[i]-S[que[hed]];
if(tep>ret) ret=tep;
while(hed<til&&dep[i]-S[i]>=dep[que[til]]-S[que[til]]) til--;
que[++til]=i;
}
return ret;
}
int main()
{
n=read();memset(cir,true,sizeof(cir));
for(int i=1;i<=n;i++)
{
int b=read(),l=read();
deg[i]++;deg[b]++;
add_e(i,b,l);add_e(b,i,l);
}
for(int i=1;i<=n;i++)
if(!vis[i])
ans+=Solve(i);
printf("%lld\n",ans);
return 0;
}