bzoj1023: [SHOI2008]cactus仙人掌图

学习了一下圆方树。

圆方树是一种可以处理仙人掌的数据结构,具体见这里:http://immortalco.blog.uoj.ac/blog/1955

简单来讲它是这么做的:用tarjan找环,然后对每个环建立一个新点,然后将环上的边删去,并环上的每个点都连到新点上。这样我们就可以把一个环缩成一个菊花图,重复这么做,一棵仙人掌就变成一棵树啦!这棵树就叫做圆方树,其中原点叫圆点,新点叫方点。

圆方树和原仙人掌很相似,而且它又是一棵树,于是我们就可以在上面dp啦!不过要注意的是对于方点的处理,不能直接更新,要作一个单调队列,因为环上有两种走法么。

期间调了很久,而且我还不会生成数据,orz cbh。

tarjan求DCC都不会写了,我好弱啊~

#include <bits/stdc++.h>
#define N 110000
using namespace std;
int n, m, nn;
vector <int> V[N], W[N];
int low[N], dfn[N], dfsnum;
int f[N];
int ans;
int shed[N], top;
deque <int> Q;
void tarjan(int t, int f)
{
    dfn[t] = low[t] = ++ dfsnum;
    shed[++ top] = t;
    for (int p = 0; p < V[t].size(); ++ p)
        if (V[t][p] != f) if (!dfn[V[t][p]])
        {
            tarjan(V[t][p], t);
            low[t] = min(low[t], low[V[t][p]]);
            if (low[V[t][p]] >= dfn[t])
            {
                if (shed[top] != V[t][p])
                {
                    nn ++;
                    int cur;
                    do
                    {
                        //W[shed[top]].push_back(nn);
                        W[nn].push_back(cur = shed[top]);
                        shed[top --] = 0;
                    }
                    while (cur != V[t][p]);
                    W[t].push_back(nn);
                    //W[nn].push_back(t);
                }
                else
                {
                    W[t].push_back(shed[top]);
                    //W[shed[top]].push_back(t);
                    shed[top --] = 0;
                }
            }
        }
        else low[t] = min(low[t], dfn[V[t][p]]);
}
void dfs(int t)
{
    f[t] = 0;
    int s = W[t].size();
    if (t > n)
    {
        for (int p = 0; p < s; ++ p)
            dfs(W[t][p]);
        for (int q = 0; q < (s + 1) / 2; ++ q)
        {
            while (!Q.empty() && f[W[t][Q.back()]] + Q.back() < f[W[t][q]] + q) Q.pop_back();
            Q.push_back(q);
        }
        for (int p = 0, q = (s + 1) / 2; p < s; ++ p, q = (q == s? 0: q + 1) )
        {
            if (Q.front() == p) Q.pop_front();
            if (q != s)
            {
                while (!Q.empty() && f[W[t][Q.back()]] + Q.back() < f[W[t][q]] + q) Q.pop_back();
                Q.push_back(q);
            }
            if (!Q.empty()) ans = max(ans, f[W[t][p]] + f[W[t][Q.front()]] + (Q.front() > p? Q.front() - p: Q.front() + s + 1 - p));
        }
        for (int p = 0; p < (s + 1) / 2; ++ p) f[t] = max(f[t], f[W[t][p]] + p);
        for (int p = (s + 1) / 2; p < s; ++ p) f[t] = max(f[t], f[W[t][p]] + s - 1 - p);
        Q.clear();
    }
    else
    {
        int mx1 = 0, mx2 = 0;
        for (int p = 0; p < s; ++ p)
        {
            dfs(W[t][p]);
            if (f[W[t][p]] + 1 > mx1) mx2 = mx1, mx1 = f[W[t][p]] + 1;
            else if (f[W[t][p]] + 1 > mx2) mx2 = f[W[t][p]] + 1;
        }
        ans = max(ans, mx1 + mx2);
        f[t] = mx1;
    }
}
int main()
{
    scanf("%d%d", &n, &m);
    for (int i = 1; i <= m; ++ i)
    {
        int k, a;
        scanf("%d%d", &k, &a);
        for (int i = 2; i <= k; ++ i)
        {
            int b;
            scanf("%d", &b);
            V[a].push_back(b);
            V[b].push_back(a);
            a = b;
        }
    }
    nn = n;
    tarjan(1, 0);
    //puts("haha");
    dfs(1);
    printf("%d\n", ans);
}
时间: 2024-10-17 07:57:22

bzoj1023: [SHOI2008]cactus仙人掌图的相关文章

bzoj千题计划113:bzoj1023: [SHOI2008]cactus仙人掌图

http://www.lydsy.com/JudgeOnline/problem.php?id=1023 dp[x] 表示以x为端点的最长链 子节点与x不在同一个环上,那就是两条最长半链长度 子节点与x在同一个环上,环形DP,单调队列优化 对于每一个环,深度最小的那个点 有可能会更新 上层节点, 所以 每一个环DP完之后,更新 dp[深度最小的点] #include<cstdio> #include<iostream> #include<algorithm> using

[bzoj1023][SHOI2008]cactus 仙人掌图 (动态规划)

Description 如果某个无向连通图的任意一条边至多只出现在一条简单回路(simple cycle)里,我们就称这张图为仙人图(cactus).所谓简单回路就是指在图上不重复经过任何一个顶点的回路. 举例来说,上面的第一个例子是一张仙人图,而第二个不是——注意到它有三条简单回路:(4,3,2,1,6,5,4). (7,8,9,10,2,3,7)以及(4,3,7,8,9,10,2,1,6,5,4),而(2,3)同时出现在前两个的简单回路里.另外,第三张图也 不是仙人图,因为它并不是连通图.显

bzoj1023 [SHOI2008]cactus仙人掌图 &amp; poj3567 Cactus Reloaded——求仙人掌直径

题目:https://www.lydsy.com/JudgeOnline/problem.php?id=1023    http://poj.org/problem?id=3567 仙人掌!直接模仿 lyd 的代码: 大概就是 tarjan 找环 + 单调队列优化 dp,然后缩环成链继续递归: 直接模仿着写的,感觉好妙啊: 不太明白边为什么要开成点数的4倍. 代码如下: #include<iostream> #include<cstdio> #include<cstring&

bzoj 1023: [SHOI2008]cactus仙人掌图 tarjan索环&amp;&amp;环上单调队列

1023: [SHOI2008]cactus仙人掌图 Time Limit: 1 Sec  Memory Limit: 162 MBSubmit: 1141  Solved: 435[Submit][Status] Description 如果某个无向连通图的任意一条边至多只出现在一条简单回路(simple cycle)里,我们就称这张图为仙人图(cactus).所谓简单回路就是指在图上不重复经过任何一个顶点的回路. 举例来说,上面的第一个例子是一张仙人图,而第二个不是——注意到它有三条简单回路

【BZOJ 1023】 [SHOI2008]cactus仙人掌图

1023: [SHOI2008]cactus仙人掌图 Time Limit: 1 Sec  Memory Limit: 162 MB Submit: 1235  Solved: 482 [Submit][Status] Description 如果某个无向连通图的任意一条边至多只出现在一条简单回路(simple cycle)里,我们就称这张图为仙人图(cactus).所谓简单回路就是指在图上不重复经过任何一个顶点的回路. 举例来说,上面的第一个例子是一张仙人图,而第二个不是--注意到它有三条简单

bzoj 1023: [SHOI2008]cactus仙人掌图 2125: 最短路 4728: 挪威的森林 静态仙人掌上路径长度的维护系列

%%% http://immortalco.blog.uoj.ac/blog/1955 一个通用的写法是建树,对每个环建一个新点,去掉环上的边,原先环上每个点到新点连边,边权为点到环根的最短/长路长度 1023 求仙人掌直径 树形dp,维护每个点向下的最长和次长路径长度,对原有的点直接更新答案,对新点可以把对应环上的点取出,倍长,破环成链,并用单调队列正反各扫一次 #include<cstdio> char buf[5000000],*ptr=buf-1; int _(){ int x=0,c

BZOJ.1023.[SHOI2008]cactus仙人掌图(DP)

题目链接 类似求树的直径,可以用(类似)树形DP求每个点其子树(在仙人掌上就是诱导子图)最长链.次长链,用每个点子节点不同子树的 max{最长链}+max{次长链} 更新答案.(不需要存次长链,求解过程中先更新ans,然后再更新最长链即可) 设f[i]为点i的诱导子图中最长链的长度. 对于环,我们找一个环上dep[]最小的点x代表这个环 看做一个点(dep为按DFS顺序更新的),求出f[x],环以外的部分像树一样直接做就可以. 对于环的处理:f[x]比较显然,f[x]=max{f[v]+dis(

bzoj 1023 [SHOI2008]cactus仙人掌图 ( poj 3567 Cactus Reloaded )——仙人掌直径模板

题目:https://www.lydsy.com/JudgeOnline/problem.php?id=1023 http://poj.org/problem?id=3567 因为lyd在讲课,所以有了lyd的模板.感觉人家写得好好呀!于是学习(抄)了一下.可以记一记. 反正各种优美.那个dp断环成链的地方那么流畅自然!tarjan里的那些 if 条件那么美! 不过十分不明白为什么边要开成4倍的.开成2倍的真的会RE.怎么分析仙人掌的边数? #include<iostream> #includ

bzoj 1023: [SHOI2008]cactus仙人掌图

这个题真的不会啊 简而言之就是树形DP+环形Dp 1 #include<cstdio> 2 #include<iostream> 3 #include<cstring> 4 #include<cstdlib> 5 #include<cmath> 6 #include<queue> 7 #include<algorithm> 8 #include<vector> 9 #define M 50008 10 #def