「BZOJ 1791」「IOI 2008」Island「基环树」

题意

求基环树森林所有基环树的直径之和

题解

考虑的一个基环树的直径,只会有两种情况,第一种是某个环上结点子树的直径,第二种是从两个环上结点子树内的最深路径,加上环上这两个结点之间的较长路径。

那就找环,然后环上每个结点做树形\(dp\)。然后把环断成长度为\(2n\)的链,记录环上的前缀和\(sum\)。假设结点\(u\)子树内最深路径为\(dep[u]\),那么就是求\(max(sum[i] - sum[j] + dep[i] + dep[j]),j < i\)。这个就转换成\(max(sum[i] + dep[i] + dep[j] - sum[j])\),用单调队列维护最大的\(dep[j] - sum[j]\)就行,类似滑动窗口。

#include <algorithm>
#include <cstring>
#include <cstdio>
#include <vector>
#include <queue>
using namespace std;

typedef long long ll;

const int N = 1e6 + 5;

struct Edge {
	int v, nxt, w;
} e[N << 1];
int hd[N], edge_num;

void link(int u, int v, int w) {
	e[edge_num] = (Edge) {v, hd[u], w};
	hd[u] = edge_num ++;
}

int n, loop[N], cnt, idx, dfn[N];
int fa[N], faw[N], loopw[N];
ll dep1[N], dep2[N], tree_d;
ll sum[N << 1], dep[N << 1];
bool onloop[N];
deque<int> q;

void dfs(int u, int cur = -1) {
	dfn[u] = ++ idx;
	for(int i = hd[u]; ~ i; i = e[i].nxt) {
        if(cur == i) continue ;
		int v = e[i].v;
		if(dfn[v]) {
			if(dfn[v] > dfn[u] || cnt) continue ;
            cnt ++;
            loop[cnt] = u;
            loopw[cnt] = e[i].w;
			for(int j = u; j != v; j = fa[j]) {
                cnt ++;
                loop[cnt] = fa[j];
                loopw[cnt] = faw[j];
            }
		} else fa[v] = u, faw[v] = e[i].w, dfs(v, i ^ 1);
	}
}

void dp(int u, int f = 0) {
	dep1[u] = dep2[u] = 0;
	for(int i = hd[u]; ~ i; i = e[i].nxt) {
		int v = e[i].v;
		if(v == f || onloop[v]) continue ;
		dp(v, u);
		ll dis = e[i].w + dep1[v];
		if(dis > dep1[u]) {
            dep2[u] = dep1[u];
            dep1[u] = dis;
        } else if(dis > dep2[u]) {
            dep2[u] = dis;
        }
	}
	tree_d = max(tree_d, dep1[u] + dep2[u]);
}

int main() {
	scanf("%d", &n);
    fill(hd + 1, hd + n + 1, -1);
	for(int i = 1, v, w; i <= n; i ++) {
		scanf("%d%d", &v, &w);
		link(v, i, w);
		link(i, v, w);
	}
	ll ans = 0, d;
	for(int i = 1; i <= n; i ++)
		if(!dfn[i]) {
            cnt = 0; dfs(i);
			for(int j = 1; j <= cnt; j ++)
				onloop[loop[j]] = 1;
			d = 0;
			for(int j = 1; j <= cnt; j ++) {
				tree_d = 0;
				dp(loop[j]);
				dep[j] = dep[j + cnt] = dep1[loop[j]];
				d = max(d, tree_d);
			}
			for(int j = 1; j <= cnt << 1; j ++)
				sum[j] = sum[j - 1] + loopw[j <= cnt ? j : j - cnt];
			q.clear();
			for(int j = 1; j <= cnt << 1; j ++) {
				//sum_j + dep_j + dep_i - sum_i
				for(; !q.empty() && q.front() + cnt - 1 < j; q.pop_front()) ;
				ll c = dep[j] - sum[j];
				if(!q.empty()) d = max(d, sum[j] + dep[j] + dep[q.front()] - sum[q.front()]);
				for(; !q.empty() && dep[q.back()] - sum[q.back()] <= c; q.pop_back()) ;
				q.push_back(j);
			}
			ans += d;
		}
	printf("%lld\n", ans);
	return 0;
}

什么,\(\text{BZOJ}\)\(10^6\)会爆栈?那只能手工栈了。。

什么,手工栈会\(\text{MLE}\)?卡卡卡

然后代码就十分难看了(

\(block\)函数是把\(dfs\)到的结点的\(head\)复制一遍。

#include <algorithm>
#include <bitset>
#include <cstdio>
using namespace std;

typedef long long ll;

const int N = 1e6 + 5;

struct Edge {
	int v, nxt, w;
} e[N << 1];
int hd[N], h[N], edge_num;

void link(int u, int v, int w) {
	e[edge_num] = (Edge) {v, hd[u], w};
	hd[u] = edge_num ++;
}

int n, loop[N], loopw[N], cnt, idx, dfn[N];
int fa[N], faw[N], q[N << 1], node[N], qn, ql, qr;
ll tree_d, sum[N << 1], dep[N];
bitset<N> onloop, vis;

void block(int s) {
    ql = qr = qn = 0;
    vis[s] = 1; q[qr ++] = s; node[qn ++] = s;
    int u, i;
    while(ql < qr) {
        u = q[ql ++];
        for(i = hd[u]; ~ i; i = e[i].nxt)
            if(!vis[e[i].v]) {
                vis[e[i].v] = 1;
                q[qr ++] = e[i].v;
                node[qn ++] = e[i].v;
            }
    }
    for(i = 0; i < qn; i ++) vis[node[i]] = 0;
}

int st[N][2], top;
void dfs(int s) {
    block(s);
    int i, j, u, v, cur;
    for(i = 0; i < qn; i ++) h[node[i]] = hd[node[i]];
    top = 0; st[top][0] = s; st[top][1] = -1; top ++;
    while(top >= 1) {
        u = st[top - 1][0]; cur = st[top - 1][1];
        if(!dfn[u]) dfn[u] = ++ idx;
        if(h[u] == -1) { top --; continue ; }
        for(int &i = h[u]; ~ i; i = e[i].nxt) {
            if(i == cur) continue ;
            v = e[i].v;
            if(!dfn[v]) {
                fa[v] = u; faw[v] = e[i].w;
                st[top][0] = v; st[top][1] = i ^ 1; top ++;
                i = e[i].nxt;
                break ;
            } else if(dfn[v] < dfn[u]) {
                cnt ++;
                loop[cnt] = u;
                loopw[cnt] = e[i].w;
                onloop[u] = 1;
                for(j = u; j != v; j = fa[j]) {
                    cnt ++;
                    loop[cnt] = fa[j];
                    loopw[cnt] = faw[j];
                    onloop[fa[j]] = 1;
                }
            }
        }
    }
}

void block2(int s) {
    ql = qr = qn = 0;
    vis[s] = 1; q[qr ++] = s; node[qn ++] = s;
    int u, i;
    while(ql < qr) {
        u = q[ql ++];
        for(i = hd[u]; ~ i; i = e[i].nxt)
            if(!vis[e[i].v] && !onloop[e[i].v]) {
                vis[e[i].v] = 1;
                q[qr ++] = e[i].v;
                node[qn ++] = e[i].v;
            }
    }
    for(i = 0; i < qn; i ++) vis[node[i]] = 0;
}

ll dep1[N], dep2[N];
ll dp(int s) {
    block2(s);
    int i, j, u, v, cur; ll dis, dep2;
    for(i = 0; i < qn; i ++) h[node[i]] = hd[node[i]];
    top = 0; st[top][0] = s; st[top][1] = -1; top ++;
    while(top >= 1) {
        u = st[top - 1][0]; cur = st[top - 1][1];
        if(h[u] == -1) {
            dep2 = dep1[u] = 0;
            for(int i = hd[u]; ~ i; i = e[i].nxt) {
                if((v = e[i].v) == cur || onloop[v]) continue ;
                dis = e[i].w + dep1[v];
                if(dis > dep1[u]) {
                    dep2 = dep1[u];
                    dep1[u] = dis;
                } else if(dis > dep2) {
                    dep2 = dis;
                }
                tree_d = max(tree_d, dep1[u] + dep2);
            }
            top --;
            continue ;
        }
        for(int &i = h[u]; ~ i; i = e[i].nxt) {
            if((v = e[i].v) == cur || onloop[v]) continue ;
            st[top][0] = v; st[top][1] = u; top ++;
        }
    }
    return dep1[s];
}

int main() {
	scanf("%d", &n);
    fill(hd + 1, hd + n + 1, -1);
    int i, j, v, w;
	for(i = 1; i <= n; i ++) {
		scanf("%d%d", &v, &w);
		link(v, i, w); link(i, v, w);
	}
	ll ans = 0, d, c;
	for(i = 1; i <= n; i ++)
		if(!dfn[i]) {
            cnt = d = 0; dfs(i);
			for(j = 1; j <= cnt; j ++) {
				tree_d = 0;
				dep[j] = dp(loop[j]);
				d = max(d, tree_d);
			}
			for(j = 1; j <= cnt << 1; j ++)
				sum[j] = sum[j - 1] + loopw[j <= cnt ? j : j - cnt];
			ql = qr = 0;
            #define _dep(u) (u <= cnt ? dep[u] : dep[u - cnt])
			for(j = 1; j <= cnt << 1; j ++) {
				//sum_j + dep_j + dep_i - sum_i
				for(; ql < qr && q[ql] + cnt - 1 < j; ql ++) ;
				c = _dep(j) - sum[j];
				if(ql < qr) d = max(d, sum[j] + _dep(j) + _dep(q[ql]) - sum[q[ql]]);
				for(; ql < qr && _dep(q[qr - 1]) - sum[q[qr - 1]] <= c; qr --) ;
				q[qr ++] = j;
			}
            #undef _dep
			ans += d;
		}
	printf("%lld\n", ans);
	return 0;
}

posted @ 2019-02-09 14:17  hfhongzy  阅读(214)  评论(0编辑  收藏  举报