BZOJ1023 [SHOI2008]cactus仙人掌图(圆方树)

题目链接

思路:
             圆方树学习中。。。树边还是那样处理,对于环的话这个可以知道一个点不会绕远路去另一点,所以应该和环的一半的距离以内的点取直径最大值,单调队列处理就行了。

#include<bits/stdc++.h>
typedef long long ll;
const int maxn = 2e5 + 10;
const int INF = 1e9 + 7;
using namespace std;

typedef pair<int, int> edge;
int n, m, T, kase = 1;
vector<int> G[maxn], bcc[maxn];
int dp[maxn], dfn[maxn], dfs_clock, tot;
int pre[maxn], bccno[maxn], bcc_cnt;
int s[maxn], que[maxn];
stack<edge> S;
vector<int> et[maxn];

int dfs(int u, int fa) {
    int lowu = pre[u] = ++dfs_clock, child = 0;
    for(int i = 0; i < G[u].size(); i++) {
        int v = G[u][i];
        edge e = edge(u, v);
        if(!pre[v]) {
            S.push(e); child++;
            int lowv = dfs(v, u);
            lowu = min(lowu, lowv);
            if(lowv >= pre[u]) {
                bcc_cnt++; bcc[bcc_cnt].clear();
                while(1) {
                    edge x = S.top(); S.pop();
                    if(bccno[x.second] != bcc_cnt) { bcc[bcc_cnt].push_back(x.second); bccno[x.second] = bcc_cnt; } 
                    if(bccno[x.first] != bcc_cnt) { bcc[bcc_cnt].push_back(x.first); bccno[x.first] = bcc_cnt; }
                    if(x.first == u && x.second == v) break;
                }
                //cout << "bcc_cnt = " << bcc_cnt << endl;
                // for(int j = 0; j < bcc[bcc_cnt].size(); j++) cout << bcc[bcc_cnt][j] << " "; cout << endl;
                if(bcc[bcc_cnt].size() == 2) {
                    et[u].push_back(v);  //树边
                    et[v].push_back(u);
                } else {
                    //环边
                    tot++;
                    for(int i = 0; i < bcc[bcc_cnt].size(); i++) {
                        int v = bcc[bcc_cnt][i];
                        et[v].push_back(tot);
                        et[tot].push_back(v);
                    }
                }
            }
        } else if(pre[v] < pre[u] && v != fa) {
            S.push(e); lowu = min(lowu, pre[v]);
        }
    }
    return lowu;
}

int ans;
void solve(int x, int fa) {
    if(x <= n) {
        dp[x] = 1;
        for(int i = 0; i < et[x].size(); i++) {
            if(et[x][i] == fa) continue;
            solve(et[x][i], x);
            if(et[x][i] <= n) {
                ans = max(ans, dp[x] + dp[et[x][i]]);
                dp[x] = max(dp[x], dp[et[x][i]] + 1);
            }
        } 
        ans = max(ans, dp[x]);
    } else {
        int len = et[x].size(); //len: 环的大小
        for(int i = 0; i < et[x].size(); i++) {
            int v = et[x][i]; if(v == fa) continue;
            solve(v, x);
        }
        int l = 0, r = 0, cnt = 2 * len, sz = len / 2;
        for(int i = 0; i < len; i++) s[i] = s[i + len] = et[x][i];
        for(int i = 0; i < sz; i++) {
            int k = dp[s[i]] - i;
            while(r > l && dp[s[que[r - 1]]] - que[r - 1] <= k) r--;
            que[r++] = i;
        }
        for(int i = sz; i < cnt; i++) {
            int k = dp[s[i]] - i;
            while(l < r && i - que[l] > sz) l++;
            ans = max(ans, dp[s[i]] + i + dp[s[que[l]]] - que[l] - 1);    
            while(r > l && dp[s[que[r - 1]]] - que[r - 1] <= k) r--;
            que[r++] = i;
        }
        for(int i = 0; i < et[x].size(); i++) {
            int v = et[x][i]; if(v == fa) continue;
            dp[fa] = max(dp[fa], dp[v] + min(i, len - i));
        }
    }
}

int main() {
    ios::sync_with_stdio(0);
    cin >> n >> m;
    for(int i = 1; i <= m; i++) {
        int u, v, t; cin >> t;
        cin >> u;
        for(int j = 1; j < t; j++) {
            cin >> v;
            G[u].push_back(v);
            G[v].push_back(u);
            u = v;
        }
    }
    tot = n;
    dfs(1, -1); solve(1, -1);
    cout << ans - 1 << endl;
    return 0;
}
/*
12 2
12 1 2 3 4 5 6 7 8 9 10 11 1
2 1 12
*/
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值