为什么我感觉所有树上简单问题到了基环树上都成了毒瘤……
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;
}