[题解][IOI2008]岛屿Island(基环树+dp

调了一万年......先是==写成=,然后快读出锅,手写队列以为出锅改成stl结果发现stl才是真正出锅....改回手写队列才a

抄题解都抄了好久好久...(写法不唯一...

然后BZOJ RE.........WDNMD

思路,找环,子树求直径,单调队列dp,比较好理解,但是写起来有点麻烦

这里用dfs找环,传bool型返回值,dfs时因为有重边不能直接记录fa,要按边记录,过程中把环上的点塞到数组里,记录每个点的下标和边权前缀和,并注意记录环的头尾衔接点

子树求直径用全局标记数组v2,环上的点在找环的时候已经在v2标记过了不用考虑,然后正常树形dp

单调队列dp,因为数组不清空,所以每次记录一下当前子树对应的下标起始点为st,dp的时候减一下偏移量

#include<iostream>
#include<cstring>
#include<cstdio>
#include<queue>
#define ll long long
using namespace std;
const int maxn=1000009;
inline int read(){
    int ret=0,fix=1;char ch;
    while(!isdigit(ch=getchar()))fix=ch=='-'?-1:fix;
    do ret=(ret<<1)+(ret<<3)+ch-'0';
    while(isdigit(ch=getchar()));
    return fix*ret;
}
int n,st;//st为本子树中环开始编号 
struct node{
    int v,nxt,w;
}e[maxn<<1];
int head[maxn],cnt=1;
inline void add(int u,int v,int w){
    e[++cnt].v=v;e[cnt].nxt=head[u];head[u]=cnt;e[cnt].w=w;
    e[++cnt].v=u;e[cnt].nxt=head[v];head[v]=cnt;e[cnt].w=w;
}
//找环 
int v[maxn],v2[maxn],id[maxn],tot;
ll s[maxn<<1];
//v=1走过,v=2环的头尾衔接点 
//v2=1所在基环树走过, 
//id原始编号 
//s为边长度前缀和,dp用 
bool dfs(int x,int last){
    if(v[x]==1){
        v[x]=2;id[++tot]=x,v2[x]=1;
        return 1;
    }
    v[x]=1;
    for(int i=head[x];i;i=e[i].nxt){
        if(i!=(last^1)&&dfs(e[i].v,i)){
            if(v[x]!=2)id[++tot]=x,v2[x]=1,s[tot]=s[tot-1]+e[i].w;
            else{
                s[st-1]=s[st]-e[i].w;
                return 0;
            }
            return 1;
        }
    }
    return 0;
}
//子树直径 
ll d[maxn],ans,ans2;
void subdp(int x){
    v2[x]=1;
    for(int i=head[x];i;i=e[i].nxt){
        int y=e[i].v;
        if(v2[y])continue;
        subdp(y);
        ans=max(ans,d[x]+d[y]+e[i].w);
        d[x]=max(d[x],d[y]+e[i].w);
    }
}
//带环直径 
ll f[maxn<<1],ans3;
int q[maxn<<1],hd=1,tl;
void dp(){
    hd=1,tl=0;
    for(int i=st;i<=tot;i++){//环复制成链 
        f[i+tot-st+1]=f[i]=d[id[i]];
        s[i+tot-st+1]=s[i+tot-st]+s[i]-s[i-1];
    }
    for(int i=st;i<=tot*2-st+1;i++){
        while(hd<=tl && q[hd]<=i-tot+st-1)hd++;
        if(hd<=tl)ans3=max(ans3,f[i]+f[q[hd]]+s[i]-s[q[hd]]);
        while(hd<=tl && f[q[tl]]-s[q[tl]]<=f[i]-s[i])tl--;
        q[++tl]=i;
    }
//    deque<int>q;
//    for(int i=st;i<=tot*2-st+1;i++){
//        while(!q.empty() && q.front()<=i-tot+st-1)q.pop_front();
//        if(!q.empty())ans3=max(ans3,f[i]+f[q.front()]+s[i]-s[q.front()]);
//        while(!q.empty() && f[q.front()]-s[q.front()]<=f[i]-s[i])q.pop_back();
//        q.push_back(i);
//    }
}
ll work(int x){
    st=tot+1,ans2=0,ans3=0;
    //找环 
    dfs(x,0);
    //子树直径 
    for(int i=st;i<=tot;i++){
        ans=0;
        subdp(id[i]);
        ans2=max(ans2,ans);
//        f[i+tot-st+1]=f[i]=d[id[i]];
//        s[i+tot-st+1]=s[i+tot-st]+s[i]-s[i-1];
    }
    dp();
    return max(ans2,ans3);
}
int main(){
    scanf("%d",&n);
//    for(int i=1;i<=n;i++)add(i,read(),read());
    for(int i=1,u,v;i<=n;i++){
        scanf("%d%d",&u,&v);
        add(i,u,v);
    }
    ll anss=0;
    for(int i=1;i<=n;i++)
    if(!v2[i])anss+=work(i);
    printf("%lld",anss);
//    for(int i=1;i<=n;i++)printf("%d ",d[i]);
}

 

转载于:https://www.cnblogs.com/superminivan/p/11459406.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值