「IOI2008」Islands 题解报告


先占个坑,逊完了再补...

#include<bits/stdc++.h>
#define ll long long
using namespace std;
const ll N=1e7+2;
ll n,tot,cnt,ans,anss,st,ans2,ans3;
ll w[N<<1],fir[N],to[N<<1],nxt[N<<1],vis[N],v2[N],r[N],d[N],dp[N<<1],s[N];
void add(ll x,ll y,ll z){
    w[++tot]=z;
	to[tot]=y;
	nxt[tot]=fir[x];
	fir[x]=tot;
}
bool dfs(ll u,ll v){//dfs找环
    if(vis[u]==1){//找到衔接点 :走到走过的节点 
        vis[u]=2;//遍历到2次 
		r[++cnt]=u;//环上第cnt个点 
		v2[u]=1;//环上的点 
        return 1;
    }
    vis[u]=1;//维护访问数组 
    for(ll i=fir[u];i;i=nxt[i]){
    	//v:刚走过的边
		//i:现在走的边 
		//((v-1)^1)+1:与x相邻的数,指向双向边的另一条 
		//若v为偶,值为较小奇数,即v-1
		//若v为奇,值为较大偶数,即v+1 
		//等同于v+((v&1)?1:-1) 
    	if(i==((v-1)^1)+1) continue; //通向父亲 
    	//循环到刚才走过的那条边,跳过 
    	if(dfs(to[i],i)){//当前节点在环上
            if(vis[u]!=2) {//当前节点不是衔接点 
            	r[++cnt]=u;//环上的cnt号点原始编号 
				v2[u]=1;//u节点所在的基环树 1:走过 
				s[cnt]=s[cnt-1]+w[i];//前缀
				return 1;
            } else {//是衔接点 
                s[st-1]=s[st]-w[i];//前缀 
                //将多走的一条边删掉 
                return 0;
            }
        }
    }    
    return 0;		
}
void trdp(ll u){//case1//dp直径 
    v2[u]=1;
    for(ll i=fir[u];i;i=nxt[i]){
        ll v=to[i];
        if(v2[v])
            continue;
        trdp(v);
        ans=max(ans,d[u]+d[v]+w[i]);
        d[u]=max(d[u],d[v]+w[i]);
    }
}
ll solve(ll rt){
    st=cnt+1,ans2=0,ans3=0;
    dfs(rt,0);
    //环上节点数量cnt-st+1 
    for(ll i=st;i<=cnt;i++){
        ans=0;
        trdp(r[i]);
        ans2=max(ans2,ans);//case 1:在一棵子树上 
        dp[i+cnt-st+1]=dp[i]=d[r[i]];//二倍,断环为链 
        s[i+cnt-st+1]=s[i+cnt-st]+s[i]-s[i-1];
        //节点i对应的另一个节点即为i+cnt-st+1 
    }
    deque<ll> q;//双端队列 
    for(ll i=st;i<=2*cnt-st+1;i++){
        while(!q.empty()&&q.front()<=i-cnt+st-1) q.pop_front();
        if(!q.empty()) ans3=max(ans3,dp[i]+dp[q.front()]+s[i]-s[q.front()]);
        while(!q.empty()&&dp[q.back()]-s[q.back()]<=dp[i]-s[i]) q.pop_back();
        q.push_back(i);
    }//case2
    return max(ans2,ans3);//取优解 
}
int main(){
    scanf("%lld",&n);
    for(ll x=1,y,z;x<=n;x++){
        scanf("%lld%lld",&y,&z);
        add(x,y,z),add(y,x,z);
    }//输入
    for(ll i=1;i<=n;i++){
    	if(!v2[i])//保证遍历每个节点 
            anss+=solve(i);
    }
    printf("%lld",anss);
    return 0;
}
posted @ 2022-06-08 20:35  _Youngxy  阅读(67)  评论(0)    收藏  举报