题解 P4381 【[IOI2008]Island】

题目链接

为什么我感觉所有树上简单问题到了基环树上都成了毒瘤……

Solution [IOI2008]Island

题目大意:给定基环树森林,求每棵树的直径之和

动态规划-树形dp,单调队列,基环树


分析:

首先如果把"基环"去掉就是sb题

我们用(f[u])表示以(u)为根的子树的最长链,假定有边(e)

(f[u]=max{f[e.to]+e.dist})

(ans[u])表示以(u)为根的子树的直径

(ans[u]=max{ans[e.to],f'[u] + f[e.to]+e.dist})

一棵树直径有两种可能,它子树的直径和过它根的直径,由子树(ans)转移不解释,(f')代表更新前的(f),我们用之前子树的最长链加上当前子树的最长链得到第二种情况

树形(dp)可以(O(n))解决

然后在基环树上,直径分两种,不经过环的直径和经过环的直径

不经过环的直径我们在环上点的(ans)中找个(max)即可

如果我们要求经过环的直径,假定(u,v)均在环上,显然(ans=max{f[u]+f[v]+dis(u,v)}),其中(dis(u,v))为环上两点两条路径中较长的那条

这玩意儿如果暴力是(O(n^3))的,加上前缀和可以做到(O(n^2))

由于同时考虑两个方向不好做,我们断环成链

(ans=max{f[u]+f[v]+sum[u]-sum[v] quad v<u}),我们把环上距离做个前缀和得到(sum),然后这个东西就可以用单调队列来优化到(O(n))

窝学艺不精导致的玄学(bug:)

(0)小于一个负数

问题:使用了(vector)所致,(vector.size())返回值(size\_t),该类型为无符号整数,然后有符号数和无符号数运算会统一成无符号数运算……无符号数存负数就会(GG)

(Trick:)题目保证每个点出度为(1),如果我们建反图就是一棵外向树

#include <cstdio>
#include <cctype>
#include <vector>
#include <algorithm>
#include <queue>
using namespace std;
const int maxn = 1e6 + 100;
typedef long long ll;
inline int read(){
	int x = 0;char c = getchar();
	while(!isdigit(c))c = getchar();
	while(isdigit(c))x = x * 10 + c - '0',c = getchar();
	return x;
}
struct Edge{int to,dist;};
vector<Edge> G[maxn],rG[maxn];
vector<int> cir[maxn];
int deg[maxn],vis[maxn],iscir[maxn],q[maxn << 1],head,tail,n,cir_tot;
ll f[maxn],ans[maxn],sum[maxn << 1],val[maxn << 1],ansout;
inline void dfs(int u){
	for(auto e : G[u]){
		if(iscir[e.to])continue;
		dfs(e.to);
		ans[u] = max(ans[u],f[u] + f[e.to] + e.dist);
		ans[u] = max(ans[u],ans[e.to]);
		f[u] = max(f[u],f[e.to] + e.dist);
	}
}
inline void topo(){
	queue<int> Q;
	for(int i = 1;i <= n;i++){
		iscir[i] = 1;	
		if(!deg[i])Q.push(i);
	}
	while(!Q.empty()){
		int u = Q.front();Q.pop();
		iscir[u] = 0;
		for(auto e : rG[u])
			if(!--deg[e.to])Q.push(e.to);
	}
	for(int i = 1;i <= n;i++)
		if(iscir[i] && !vis[i]){
			int now = i;cir_tot++;
			do{
				for(auto e : G[now])
					if(iscir[e.to]){
						cir[cir_tot].push_back(e.to);
						vis[now = e.to] = 1;break;
					}
			}while(now != i);
		}
}
inline void solve(){
	for(int id = 1;id <= cir_tot;id++){
		reverse(cir[id].begin(),cir[id].end());
		ll tmp = 0;
		for(int x : cir[id])
			dfs(x),tmp = max(tmp,ans[x]);
		for(int i = 0;i < cir[id].size();i++){
			int u = cir[id][i],pre = i == 0 ? cir[id][cir[id].size() - 1] : cir[id][i - 1];
			for(auto e : G[u])
				if(e.to == pre){sum[i] = sum[i + cir[id].size()] = e.dist;break;}
		}
		for(int i = 1;i < cir[id].size() * 2;i++)
			sum[i] += sum[i - 1];
		for(int i = 0;i < cir[id].size() * 2;i++)
			val[i] = f[cir[id][i % cir[id].size()]] - sum[i];
		q[head = tail = 1] = 0;
		for(int i = 1;i < cir[id].size() * 2;i++){
			while(head <= tail && q[head] <= (i - (int)cir[id].size()))head++;
			if(head <= tail)tmp = max(tmp,f[cir[id][i % cir[id].size()]] + sum[i] + val[q[head]]);
			while(head <= tail && val[q[tail]] <= val[i])tail--;
			q[++tail] = i;
		}
		ansout += tmp;
	}
}
inline void addedge(int from,int to,int dist){
	G[from].push_back(Edge{to,dist});
	deg[from]++;
	rG[to].push_back(Edge{from,0}); 
}
int main(){
	n = read();
	for(int u,v = 1;v <= n;v++)
		u = read(),deg[u]++,G[u].push_back(Edge{v,read()}),rG[v].push_back(Edge{u,0});
	topo();
	solve();
	printf("%lld
",ansout);
	return 0;
}
原文地址:https://www.cnblogs.com/colazcy/p/11818362.html