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

1023: [SHOI2008]cactus仙人掌图

Time Limit: 1 Sec  Memory Limit: 162 MB
Submit: 1141  Solved: 435
[Submit][Status]

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)同时出现在前两个的简单回路里。另外,第三张图也不是仙人图,因为它并不是连通图。显然,仙人图上的每条边,或者是这张仙人图的桥(bridge),或者在且仅在一个简单回路里,两者必居其一。定义在图上两点之间的距离为这两点之间最短路径的距离。定义一个图的直径为这张图相距最远的两个点的距离。现在我们假定仙人图的每条边的权值都是1,你的任务是求出给定的仙人图的直径。

Input

输入的第一行包括两个整数n和m(1≤n≤50000以及0≤m≤10000)。其中n代表顶点个数,我们约定图中的顶点将从1到n编号。接下来一共有m行。代表m条路径。每行的开始有一个整数k(2≤k≤1000),代表在这条路径上的顶点个数。接下来是k个1到n之间的整数,分别对应了一个顶点,相邻的顶点表示存在一条连接这两个顶点的边。一条路径上可能通过一个顶点好几次,比如对于第一个样例,第一条路径从3经过8,又从8返回到了3,但是我们保证所有的边都会出现在某条路径上,而且不会重复出现在两条路径上,或者在一条路径上出现两次。

Output

只需输出一个数,这个数表示仙人图的直径长度。

Sample Input

15 3
9 1 2 3 4 5 6 7 8 3
7 2 9 10 11 12 13 10
5 2 14 9 15 10 8
10 1
10 1 2 3 4 5 6 7 8 9 10

Sample Output

9

HINT

对第一个样例的说明:如图,6号点和12号点的最短路径长度为8,所以这张图的直径为8。

【注意】使用Pascal语言的选手请注意:你的程序在处理大数据的时候可能会出现栈溢出。如果需要调整栈空间的大小,可以在程序的开头填加一句:{$M 5000000},其中5000000即指代栈空间的大小,请根据自己的程序选择适当的数值。

  这种仙人掌一个点可能在多个环中,所以处理起来会比较麻烦,看了网上的最长的题解http://z55250825.blog.163.com/blog/static/150230809201412793151890/,和最短的标程http://hzwer.com/4645.html才有了基本的思路。很多的类似图论题都是在tarjan内部就完成大部分的操作,而我总是指望tarjan完成之后才统一处理,这样就丢掉了很多有用的信息。

  注意,bzoj上srand(time(0))是要RE的

#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<queue>
#include<set>
using namespace std;
#define PROB "cactus"
#define MAXN 100010
#define MAXV MAXN
#define MAXE MAXV * 2
#define INF 0x3f3f3f3f
struct edge
{
        int x,y,id;
}e[MAXE];
int n,m;
int prv[MAXN];
struct Edge
{
        int sp,np,val,id;
        Edge *next;
}E[MAXE],*V[MAXV];
int tope;
int dfn[MAXN],dfstime=0;
int ans=0;
void addedge(int x,int y,int id,int z=1)
{
        E[++tope].np=y;
        E[tope].id=id;
        E[tope].sp=x;
        E[tope].val=z;
        E[tope].next=V[x];
        V[x]=&E[tope];
}
int len[MAXN];
int arr[MAXN];
int seq[MAXN];
int vis[MAXN];
void solve(int rt,int now)
{
        int tota=0;
        int x,i;
        x=now;
        while (x!=rt)
        {
                arr[tota++]=len[x];
                x=prv[x];
        }
        arr[tota++]=0;
        for (i=0;i<tota;i++)
                arr[i+tota]=arr[i];
        x=0;
        seq[0]=0;
        int tail=0,head=0;
        for (i=1;i<tota*2;i++)
        {
                while (i-seq[head]>tota/2)head++;
                ans=max(ans,arr[seq[head]]+(i-seq[head])+arr[i]);
                while (tail>=head && arr[seq[tail]] + (i-seq[tail]) <=arr[i])tail--;
                seq[++tail]=i;
        }
}
int low[MAXN];
bool inc[MAXN];
vector<int> ptr[MAXN];
void dfs(int now,int pnt=-1)
{
        low[now]=dfn[now]=++dfstime;
        Edge *ne;
        vis[now]=1;
        int x=-1,y=-1;
        int st=-1,rt;
        for (ne=V[now];ne;ne=ne->next)
        {
                if (ne->np==prv[now])continue;
                if (vis[ne->np]==1)
                {
                        ptr[ne->np].push_back(now);
                        low[now]=min(low[now],dfn[ne->np]);
                }else if (!vis[ne->np])
                {
                        prv[ne->np]=now;
                        dfs(ne->np,now);
                        low[now]=min(low[now],low[ne->np]);
                }
        }
        for (int i=0;i<ptr[now].size();i++)
        {
                st=ptr[now][i],rt=now;
                int sizc=1;
                x=st;
                inc[x]=true;
                while (x!=rt)
                {
                        x=prv[x];
                        inc[x]=true;
                        sizc++;
                }
                x=st;
                int d=1;
                int t=0;
                while (x!=rt)
                {
                        t=max(t,min(d,sizc-d)+len[x]);
                        d++;
                        x=prv[x];
                }
                ans=max(ans,t+len[rt]);
                len[rt]=max(len[rt],t);
                solve(rt,st);
        }
        x=y=0;
        if (!inc[now])
        {
                for (ne=V[now];ne;ne=ne->next)
                {
                        if (dfn[ne->np]<dfn[now])continue;
                        if (y<len[ne->np]+1)
                        {
                                y=len[ne->np]+1;
                                if (x<y)swap(x,y);
                        }
                }
        }
        if (low[now]==dfn[now])
        {
                if (prv[now])ans=max(ans,len[now]+1+len[prv[now]]);
                if (prv[now])len[prv[now]]=max(len[prv[now]],len[now]+1);
        }
        ans=max(ans,x+y);
        vis[now]=2;
}
int deg[MAXN];
void work()
{
        int i,x,y;
        for (i=0;i<m;i++)
        {
                x=e[i].x;
                y=e[i].y;
                addedge(x,y,i);
                addedge(y,x,i);
                deg[x]++;
                deg[y]++;
        }
        int core=0;
        for (i=1;i<=n;i++)
        {
                if (deg[i]==1)core=i;
                if (!core && deg[i]%2==1)core=i;
        }
        if (!core)core=1;
        dfs(core,core);
}
set<pair<int,int> > S;
int main()
{
        //freopen(PROB".in","r",stdin);
        //freopen(PROB".out","w",stdout);
        int l;
        scanf("%d%d",&n,&l);
        int i,j,k;
        int x,y,z;
        for (i=0;i<l;i++)
        {
                scanf("%d",&x);
                scanf("%d",&y);
                for (j=1;j<x;j++)
                {
                        scanf("%d",&z);
                        e[m].x=y;
                        e[m].y=z;
                        m++;
                        y=z;
                }
        }
        for (i=0;i<m;i++)
        {
                if (e[i].x>e[i].y)swap(e[i].x,e[i].y);
                if (e[i].x==e[i].y || S.find(make_pair(e[i].x,e[i].y))!=S.end())
                {
                        i--;m--;
                        continue;
                }else
                {
                        S.insert(make_pair(e[i].x,e[i].y));
                }
                e[i].id=i;
        }
        work();
        printf("%d\n",ans);
}
时间: 2024-10-05 05:41:38

bzoj 1023: [SHOI2008]cactus仙人掌图 tarjan索环&&环上单调队列的相关文章

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

【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千题计划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仙人掌图

学习了一下圆方树. 圆方树是一种可以处理仙人掌的数据结构,具体见这里:http://immortalco.blog.uoj.ac/blog/1955 简单来讲它是这么做的:用tarjan找环,然后对每个环建立一个新点,然后将环上的边删去,并环上的每个点都连到新点上.这样我们就可以把一个环缩成一个菊花图,重复这么做,一棵仙人掌就变成一棵树啦!这棵树就叫做圆方树,其中原点叫圆点,新点叫方点. 圆方树和原仙人掌很相似,而且它又是一棵树,于是我们就可以在上面dp啦!不过要注意的是对于方点的处理,不能直接

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&

[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)同时出现在前两个的简单回路里.另外,第三张图也 不是仙人图,因为它并不是连通图.显