zoukankan      html  css  js  c++  java
  • [题解] LuoguP4381 [IOI2008]Island

    LuoguP4381 [IOI2008]Island


    Description

    一句话题意:给一个基环树森林,求每棵基环树的直径长度的和(基环树的直径定义与树类似,即基环树上一条最长的简单路径),节点总数不超过(10^6)

    Solution

    问题就是如何求基环树的直径。

    首先树的直径的话可以直接(dp),那如果有一个环怎么办?

    这个环上会挂着几棵树,那么直径只会有两种情况

    • 不经过环上的边,即每棵树直径的最大值
    • 经过一个环,即挂在换上的两棵树(i,j)的深度和在加上(i,j)在环上的距离

    第一种情况直接树形(Dp)求一下树的直径就好了。

    第二种情况有点麻烦,为了方便下面令(tree(x))表示以(x)为根挂在环上的树,(depth(T))表示树(T)的深度,(dist(i,j))表示环上两点之间只走环上的边的最大距离((i,j)在环上只有两条路径)。

    那这种情况的答案就是(maxlimits_{i ot= j} {depth(tree(i)) + depth(tree(j)) + dist(i,j)})

    下面令(v_1,v_2,...,v_s)表示大小为(s)(点的个数)的环上以逆时针或顺时针的访问顺序依次访问到的(s)个点,(sum_i)表示从(v_1)按顺序走走到(v_i)的环上路径长度。

    那么点(i)按一个方向走到点(j)的环上长度就是(sum_j - sum_i)

    我们可以把环复制两倍,然后就能够处理第(2)个方向的距离。

    (v)变为(v_1,v_2,...,v_{s},v_{s+1},...,v_{2s}),那么(v_i)(v_j)((1 le i<j le 2s, abs(i-j) < n))的最大距离(dist(i,j))就是(max(sum_j - sum_i, sum_{i+s} - sum_j))

    这样的话就可以单调队列维护,扫一次(v_{1...2s})就行了。

    找环的话可以在(Dfs)树上找,不卡栈空间的(至少(Luogu)是这样......)

    Code

    #include <bits/stdc++.h>
    using namespace std;
    template<typename tp> inline void read(tp &x){
    	x=0; tp f=1; char ch=getchar();
    	for (;!isdigit(ch);ch=getchar())f=ch=='-'?-f:f;
    	for (;isdigit(ch);ch=getchar())x=(x<<1)+(x<<3)+(ch^48);
    	x=x*f;
    }
    #define pb push_back
    #define same(e1,e2) (min(e1^1,e1)==min(e2^1,e2))
    typedef long long ll;
    const ll INF=1e18;
    const int N=2e6+10;
    int cnt=1,fst[N],nxt[N<<1],to[N<<1];ll dis[N<<1];
    inline void ade(int x,int y,ll w){
    	to[++cnt]=y,nxt[cnt]=fst[x],fst[x]=cnt;
    	dis[cnt]=w;
    }
    inline void addedge(int x,int y,ll w){ade(x,y,w),ade(y,x,w);}
    vector<int>ring[N]; int tot=0,dep[N],fa[N];
    void dfs(int x,int deep,int lste,int prev){
    	dep[x]=deep,fa[x]=prev;
    	for (int i=fst[x];i;i=nxt[i]){
    		int v=to[i]; //printf("%d->%d
    ",x,v);
    		if (dep[v]==0) dfs(v,deep+1,i,x);
    		else if (!same(i,lste)&&dep[v]<dep[x]){
    			++tot;for (int nw=x;nw!=v;nw=fa[nw])ring[tot].pb(nw);
    			ring[tot].pb(v);
    		}
    	}
    }
    int mark[N];ll dp[N],mxdp;
    void DP(int x,int prev){
    	for (int i=fst[x];i;i=nxt[i]){
    		int v=to[i]; if (mark[v]||v==prev) continue;
    		DP(v,x);
    		mxdp=max(dp[x]+dp[v]+dis[i],mxdp);
    		dp[x]=max(dp[x],dp[v]+dis[i]);
    	}
    }
    int vis[N],id[N],tim;ll a[N],b[N];
    void getW(int x,ll dd,int lste){
    	vis[x]++,id[++tim]=x,b[tim]=dd;
    	for (int i=fst[x];i;i=nxt[i]){
    		int v=to[i]; if (!same(i,lste)&&mark[v]&&vis[v]<2)getW(v,dd+dis[i],i);
    	}
    }
    int q[N];
    ll solve(int k1){
    	int len=ring[k1].size(); ll ans=0;
    	for (int i=0;i<len;i++) mark[ring[k1][i]]=1;
    	tim=0,getW(ring[k1][0],0,0);
    	for (int i=1;i<=len;i++){
    		int x=id[i]; 
    		mxdp=0,DP(x,0),ans=max(ans,mxdp);
    		a[i]=dp[x];
    	}
    	for (int i=1;i<=len;i++) a[i+len]=a[i];
    	int l=1,r=1; q[l]=1;
    	for (int i=2;i<=tim;i++){
    		while (l<=r&&i-q[l]>=len)l++;
    		int j=q[l]; if (l<=r)ans=max(ans,a[i]+a[j]+b[i]-b[j]);
    		while (l<=r&&a[i]-b[i]>a[q[r]]-b[q[r]])r--;
    		q[++r]=i;
    	}
    	return ans;
    }
    int main(){
    	int n;read(n);
    	for (int i=1;i<=n;i++){
    		int x;ll w; read(x),read(w);
    		addedge(x,i,w);
    	}
    	for (int i=1;i<=n;i++) if (!dep[i])dfs(i,1,0,0);
    	ll ans=0;
    	for (int i=1;i<=tot;i++)ans+=solve(i);
    	printf("%lld
    ",ans);
    	return 0;
    }
    
  • 相关阅读:
    洛谷 P3355 骑士共存问题
    Redis 安装
    Java 集合:(十八) Map接口
    并发编程——Java版【目录】
    Java 集合:(十七) Queue 常用接口:BlockingQueue 子接口
    Java 集合:(十六) Queue 常用接口:Deque 子接口
    Java 集合:(十五) Queue 子接口
    Java 集合:(番外篇一) ArrayList线程不安全性
    第十三章:StringTable
    Java 集合:(十三) Set实现类:LinkedHashSet
  • 原文地址:https://www.cnblogs.com/wxq1229/p/12229747.html
Copyright © 2011-2022 走看看