[IOI2008]Island

题目大意:

找基环树直径

(这个题输入给出的是内向基环树(虽然是无向边))

 

存在两种情况:

1.直径在树上。

2.直径从树里走到环上,再走进另外一个树里。

 

首先dfs找到环。

第一种直接树形dp。dp[i]i往下最长路径。还能用来求第二种情况。

第二种,找到环之后,断环成链,复制一倍。求的是,选择距离小于环长的两个点,贡献是两个点的dp[i],和两个点之间的距离。这个用单调队列优化dp即可。

 

bzoj会爆栈mmp

如果你是ywy可以bfs求树形dp

其实,这是一个内向基环树,所以,直接topo排序,从叶子往上面topo

即可求得dp,还能求得环。

 

代码:(dfs爆栈版)

#pragma comment(linker, "/STACK:102400000,102400000")
#include<bits/stdc++.h>
#define il inline
#define reg register int 
#define numb (ch^'0')
using namespace std;
typedef long long ll;
void rd(int &x){
    char ch;x=0;
    while(!isdigit(ch=getchar()));
    for(x=numb;isdigit(ch=getchar());x=x*10+numb);
}
namespace Miracle{
const int N=1e6+5;
int n;
struct node{
    int nxt,to;
    ll val;
}e[2*N];
int hd[N],cnt=1;
void add(int x,int y,int z){
    e[++cnt].nxt=hd[x];
    e[cnt].to=y;
    e[cnt].val=z;
    hd[x]=cnt;
}
bool vis[N];
bool on[N];//on the huan
bool in[N];
int to[N],v[N];
int sta[N],top;
int len;//huan chang
int mem[2*N],tot;
ll cos[2*N];
int q[2*N],l,r;
ll f[N];
ll ans;
ll sum;
ll zhi;
bool fl;
void dfs(int x,int in_edge){
//    cout<<" dfs "<<x<<" "<<fl<<" "<<top<<endl;
    vis[x]=1;
    sta[++top]=x;
    for(reg i=hd[x];i;i=e[i].nxt){
        int y=e[i].to;
        if(i==(in_edge^1)) continue;
        if(!vis[y]){
            dfs(y,i);
        }
        else if(!fl){
            fl=true;
        //    cout<<" find "<<top<<" : "<<sta[top]<<endl;
            int z;
            do{
                z=sta[top];
                on[z]=1;
                mem[++len]=z;
                top--;
            }while(z!=y);
        }
    }
    if(sta[top]==x) --top;//warning!!!
}
void dp(int x,int fa){//find farthest
    for(reg i=hd[x];i;i=e[i].nxt){
        int y=e[i].to;
        if(y==fa) continue;
        if(on[y]) continue;
        dp(y,x);
        zhi=max(zhi,f[x]+e[i].val+f[y]);
        f[x]=max(f[x],e[i].val+f[y]);
    }
}
ll wrk(int st){
    sum=0;
    len=0;
    fl=false;top=0;
    dfs(st,0);
    
    for(reg i=1;i<=len;++i){
        zhi=0;
        dp(mem[i],0);
        sum=max(sum,zhi);
        mem[i+len]=mem[i];
    }
    l=1,r=0;
    cos[1]=0;
    for(reg i=2;i<=2*len;++i){
        cos[i]=(to[mem[i]]==mem[i-1])?v[mem[i]]:v[mem[i-1]];
        cos[i]+=cos[i-1];
    }
    for(reg i=1;i<=2*len;++i){
        while(l<=r&&i-q[l]>=len) ++l;
        if(i!=1) sum=max(sum,f[mem[q[l]]]-cos[q[l]]+cos[i]+f[mem[i]]);
        while(l<=r&&f[mem[q[r]]]-cos[q[r]]<=f[mem[i]]-cos[i]) --r;
        q[++r]=i;
    }
    return sum;
}
int main(){
    rd(n);int x;int z;
    for(reg i=1;i<=n;++i){
        rd(x);rd(z);
        to[i]=x;
        v[i]=z;
        add(i,x,z);add(x,i,z);
    }
    for(reg i=1;i<=n;++i){
        if(!vis[i]){
            ans+=wrk(i);
        }
    }
    printf("%lld",ans);
    return 0;
}

}
int main(){
    Miracle::main();
    return 0;
}

/*
   Author: *Miracle*
   Date: 2018/11/4 9:27:26
*/

 

posted @ 2018-11-04 11:07  *Miracle*  阅读(283)  评论(3编辑  收藏  举报