[BZOJ1791][IOI2008]Island岛屿(环套树DP)

时间:2022-04-06 11:41:53

同NOI2013快餐店(NOI出原题?),下面代码由于BZOJ栈空间过小会RE。

大致是对每个连通块找到环,在所有内向树做一遍DP,再在环上做两遍前缀和优化的DP。

 #include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>
#define rep(i,l,r) for (int i=(l); i<=(r); i++)
#define For(i,x) for (int i=h[x],k; i; i=nxt[i])
typedef long long ll;
using namespace std; const int N=;
ll ans,res,f[N],v1[N],v2[N],u1[N],u2[N];
int dfn[N],fa[N],pre[N],h[N],a[N],b[N];
int n,x,w,cnt,tim,tot,to[N<<],val[N<<],nxt[N<<],d[N];
void add(int u,int v,int w){ to[++cnt]=v; val[cnt]=w; nxt[cnt]=h[u]; h[u]=cnt; } void dfs(int x){
dfn[x]=++tim;
For(i,x) if ((k=to[i])!=fa[x]){
if (!dfn[k]) fa[k]=x,pre[k]=val[i],dfs(k);
else if (dfn[k]>dfn[x]){
for (int t=k; t!=x; t=fa[t]) a[++tot]=t,d[t]=,b[tot]=pre[t];
a[++tot]=x; d[x]=; b[tot]=val[i];
}
}
} void Dfs(int x,int fa){
For(i,x) if ((k=to[i])!=fa && !d[k])
Dfs(k,x),ans=max(ans,f[x]+f[k]+val[i]),f[x]=max(f[x],f[k]+val[i]);
} void DP(int x){
tot=; ans=; dfs(x); ll sm=,mx=;
rep(i,,tot) Dfs(a[i],);
rep(i,,tot) d[a[i]]=;
rep(i,,tot+) u1[i]=u2[i]=v1[i]=v2[i]=;
rep(i,,tot){
sm+=b[i-]; u1[i]=max(u1[i-],f[a[i]]+sm);
v1[i]=max(v1[i-],f[a[i]]+sm+mx); mx=max(mx,f[a[i]]-sm);
ans=max(ans,v1[i]);
}
int tmp=b[tot]; sm=mx=b[tot]=;
for (int i=tot; i; i--){
sm+=b[i]; u2[i]=max(u2[i+],f[a[i]]+sm);
v2[i]=max(v2[i+],f[a[i]]+sm+mx); mx=max(mx,f[a[i]]-sm);
ans=max(ans,v2[i]);
}
rep(i,,tot-) ans=max(ans,u1[i]+u2[i+]+tmp);
res+=ans;
} int main(){
freopen("bzoj1791.in","r",stdin);
freopen("bzoj1791.out","w",stdout);
scanf("%d",&n);
rep(i,,n) scanf("%d%d",&x,&w),add(x,i,w),add(i,x,w);
rep(i,,n) if (!dfn[i]) DP(i);
printf("%lld\n",res);
return ;
}