圆方树及其应用

圆方树

圆方树是一种专门用来处理仙人掌问题的树。
(仙人掌:任意一条边最多在一个环上的无向连通图)
如何建立圆方树呢?
我们可以求出所有的点双,也就是环
对于每一个环,我们建立一个方点,代表这棵子仙人掌
(我们认为仙人掌的根为1,各种定义可以感性理解一下)
然后原图上的点就成为圆点
每个方点向它周围的圆点连边
原图上的桥都连出来
这样我们就得到了一棵结构优美的圆方树
它有一些有用的性质:
1.方点之间不会存在连边
2.方点周围的所有点刚好就是环上的所有点
3.每个点都恰好代表它的子仙人掌
然后我们就可以优美地去处理一类仙人掌问题了
(例如:仙人掌上dp,仙人掌剖分,仙人掌上分治)
建树细节还是比较多的,可以参考我的代码,自认为写得不算丑
bzoj1023代码:

#include<cstdio>
#include<cstring>
#include<iostream>
#include<cmath>
#include<algorithm>
#include<cstdlib>
#define ll long long
using namespace std;
inline int read(){
    int x=0;char ch=' ';int f=1;
    while(ch!='-'&&(ch<'0'||ch>'9'))ch=getchar();
    if(ch=='-')f=-1,ch=getchar();
    while(ch>='0'&&ch<='9')x=(x<<3)+(x<<1)+(ch^48),ch=getchar();
    return x*f;
}
const int N=2e5+5,M=N*30;
int n,m,tot,tot2,ans,cnt;
int head[N],to[M],Next[M],head2[N],to2[M],Next2[M],val[M];
int f[N],a[N],on[N],dfn[N],dfn_clock,father[N],d[N];
inline void addedge(int x,int y){to[++tot]=y;Next[tot]=head[x];head[x]=tot;}
inline void addedge2(int x,int y){to2[++tot2]=y;Next2[tot2]=head2[x];head2[x]=tot2;}
inline void dfs(int x,int fa){
    dfn[x]=++dfn_clock;d[x]=d[fa]+1;
    for(int i=head[x];i;i=Next[i]){
        int u=to[i];
        if(u==fa)continue;
        if(!dfn[u]){
            father[u]=x;
            on[x]=0;//复原on数组,为了再接下一个环
            dfs(u,x);
            if(!on[x])addedge2(x,u),addedge(u,x);
        }
        else{
            if(dfn[u]>dfn[x])continue;
            ++cnt;
            for(int j=x;j!=father[u];j=father[j])//回溯遍历
                addedge2(n+cnt,j),addedge2(j,n+cnt),on[j]=1;//加边&&标记on数组
        }
    }
}
int se[N];//记录次优值,用于更新答案
inline void Max(int x,int v){
    if(v>=f[x])se[x]=f[x],f[x]=v;
    else if(v>se[x])se[x]=v;
}
inline int Get(int x){
    return f[x]+se[x];
}
int q1[N],q2[N];
inline void dfs2(int x,int fa){
    for(int i=head2[x];i;i=Next2[i]){
        int u=to2[i];
        if(u==fa)continue;
        if(u<=n){
            dfs2(u,x);
            Max(x,f[u]+1);
        }
        else{
            //dfs每棵子仙人掌,然后做一次单调队列优化dp,同时更新环上最高点的f值
            int L=0,pos=1;
            for(int j=head2[u];j;j=Next2[j])if(to2[j]!=x)dfs2(to2[j],u);
            for(int j=head2[u];j;j=Next2[j])a[++L]=to2[j];
            int lim=L/2,tmp=L;
            for(int j=1;j<=tmp;j++)a[++L]=a[j];//将整个队列复制一遍,保证每个点都能被更新到
            int l=1,r=1,num=-1e9,dis;
            q1[l]=f[a[2]];q2[l]=2;
            for(int j=3;j<=L;j++){
                if(a[j]==x)continue;
                while(l<=r&&j-q2[l]>lim)l++;
                if(l<=r)ans=max(ans,q1[l]+f[a[j]]+j-q2[l]);
                while(l<=r&&q1[r]+j-q2[r]<=f[a[j]])r--;
                q1[++r]=f[a[j]];q2[r]=j;
            }
            for(int i=2;i<=tmp;i++)num=max(num,f[a[i]]+min(d[a[i]]-d[x],tmp-d[a[i]]+d[x]));
            Max(x,num);
        }
    }
    ans=max(ans,Get(x));
}
int main(){
    n=read();m=read();
    for(int i=1;i<=m;i++){
        int k=read()-1,last=read(),x;
        while(k--){
            x=read();
            addedge(last,x);
            addedge(x,last);
            last=x;
        }
    }
    dfs(1,0);
    dfs2(1,0);
    printf("%d\n",ans);
    return 0;
}
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值