圆方树

题解:

好像挺简单的

首先普通的圆方树处理的是仙人掌上的问题

构造圆方树的方法就是对于每个环 建立一个方点

然后原先不在环上的边,就用圆点和圆点相连

对于在环上的边,将点连向方点

然后我们来证明一个这是一棵树

1.首先这张图联通是比较显然的

2.方点数=原先环数=e-v+1

当前点数=方点+v=e+1=当前边数+1

显然满足这两条的肯定是树了

1.仙人掌最大独立集

我们知道基环树的最大独立集就是对环上每个点的树都跑一遍

再在环上跑一遍

树上类似,我们利用dfs序,将不在环上的边按照普通的树形dp处理

对于在环上的边,我们放在环的最高处处理

方法就是从最低点到最高点dp一下(注意我们之前的时候是没有把这些环边计入父亲答案的)

#include <bits/stdc++.h>
using namespace std;
#define rint register int
#define IL inline
#define rep(i,h,t) for(int i=h;i<=t;i++)
#define dep(i,t,h) for(int i=t;i>=h;i--)
#define ll long long
#define me(x) memset(x,0,sizeof(x))
namespace IO{
    char ss[1<<24],*A=ss,*B=ss;
    IL char gc()
    {
        return A==B&&(B=(A=ss)+fread(ss,1,1<<24,stdin),A==B)?EOF:*A++;
    }
    template<class T> void read(T &x)
    {
        rint f=1,c; while (c=gc(),c<48||c>57) if (c=='-') f=-1; x=(c^48);
        while (c=gc(),c>47&&c<58) x=(x<<3)+(x<<1)+(c^48); x*=f; 
    }
    char sr[1<<24],z[20]; int Z,C1=-1;
    template<class T>void wer(T x)
    {
        if (x<0) sr[++C1]='-',x=-x;
        while (z[++Z]=x%10+48,x/=10);
        while (sr[++C1]=z[Z],--Z);
    }
    IL void wer1()
    {
        sr[++C1]=' ';
    }
    IL void wer2()
    {
        sr[++C1]='\n';
    }
    template<class T>IL void maxa(T &x,T y) {if (x<y) x=y;}
    template<class T>IL void mina(T &x,T y) {if (x>y) x=y;} 
    template<class T>IL T MAX(T x,T y){return x>y?x:y;}
    template<class T>IL T MIN(T x,T y){return x<y?x:y;}
};
using namespace IO;
const int N=2e6;
int head[N],l,n,m;
struct re{
    int a,b;
}e[N*2];
IL void arr(int x,int y)
{
    e[++l].a=head[x];
    e[l].b=y;
    head[x]=l;
}
int f[N][2],cnt,fa[N],dfn[N],low[N];
void dfs(int x,int y)
{
    dfn[x]=low[x]=++cnt; fa[x]=y;
    f[x][0]=0; f[x][1]=1;
    for (rint u=head[x];u;u=e[u].a)
    {
        int v=e[u].b;
        if (v==y) continue;
        if (low[v]==dfn[x])
        {
            int f0=0,f1=0;
            for (int j=v;j!=x;j=fa[j])
            {
              int t0=f0,t1=f1;
              f1=t0+f[j][1];
              f0=MAX(t0,t1)+f[j][0];
            }
            f[x][0]+=MAX(f0,f1);
            f0=0; f1=0;
            for (int j=v;j!=x;j=fa[j])
            {
              int t0=f0,t1=f1;
              f1=t0+f[j][1];
              f0=MAX(t0,t1)+f[j][0];
              if (j==v) f1=0;
            }
            f[x][1]+=f0;
        }
        if (!dfn[v])
        { 
          dfs(v,x); low[x]=MIN(low[x],low[v]);
        }
        if (low[v]>dfn[x])
        {
            f[x][0]+=MAX(f[v][0],f[v][1]);
            f[x][1]+=f[v][0];
        }
        mina(low[x],dfn[v]);
    }
}
int main()
{
    freopen("1.in","r",stdin);
    freopen("1.out","w",stdout);
    ios::sync_with_stdio(false);
    cin>>n>>m;
    rep(i,1,m)
    {
        int x,y;
        cin>>x>>y;
        arr(x,y); arr(y,x);
    }
    dfs(1,0);
    cout<<MAX(f[1][0],f[1][1]);
    return 0; 
}

2.仙人掌直径

这个和上一个类似,依旧不用建出圆方树,区分环边和普通边就可以了

对于环拿个单调队列跑一下 在更新一下每个点到子树的最大值就好了

 

#include <bits/stdc++.h>
using namespace std;
#define rint register int
#define IL inline
#define rep(i,h,t) for(int i=h;i<=t;i++)
#define dep(i,t,h) for(int i=t;i>=h;i--)
#define ll long long
#define me(x) memset(x,0,sizeof(x))
namespace IO{
    char ss[1<<24],*A=ss,*B=ss;
    IL char gc()
    {
        return A==B&&(B=(A=ss)+fread(ss,1,1<<24,stdin),A==B)?EOF:*A++;
    }
    template<class T> void read(T &x)
    {
        rint f=1,c; while (c=gc(),c<48||c>57) if (c=='-') f=-1; x=(c^48);
        while (c=gc(),c>47&&c<58) x=(x<<3)+(x<<1)+(c^48); x*=f; 
    }
    char sr[1<<24],z[20]; int Z,C1=-1;
    template<class T>void wer(T x)
    {
        if (x<0) sr[++C1]='-',x=-x;
        while (z[++Z]=x%10+48,x/=10);
        while (sr[++C1]=z[Z],--Z);
    }
    IL void wer1()
    {
        sr[++C1]=' ';
    }
    IL void wer2()
    {
        sr[++C1]='\n';
    }
    template<class T>IL void maxa(T &x,T y) {if (x<y) x=y;}
    template<class T>IL void mina(T &x,T y) {if (x>y) x=y;} 
    template<class T>IL T MAX(T x,T y){return x>y?x:y;}
    template<class T>IL T MIN(T x,T y){return x<y?x:y;}
};
using namespace IO;
const int N=2e6;
int head[N],l,n,m;
struct re{
    int a,b;
}e[N*2];
IL void arr(int x,int y)
{
    e[++l].a=head[x];
    e[l].b=y;
    head[x]=l;
}
int f[N][2],cnt,fa[N],dfn[N],low[N],ans;
int a[N];
re p[N];
void dfs(int x,int y)
{
    dfn[x]=low[x]=++cnt; fa[x]=y;
    f[x][0]=f[x][1]=-1;
    for (rint u=head[x];u;u=e[u].a)
    {
        int v=e[u].b;
        if (v==y) continue;
        if (low[v]==dfn[x])
        {
            int cnt=0;
            for (int j=v;j!=x;j=fa[j])
              a[++cnt]=f[j][0];
            int t=0;
            rep(j,1,cnt) maxa(t,a[j]+MIN(j,cnt-j+1)-1);
            if (t>f[x][0]) f[x][1]=f[x][0],f[x][0]=t;
            else if (t>f[x][1]) f[x][1]=t;
            int len=cnt+1; 
            a[++cnt]=0;
            rep(j,1,cnt) a[cnt+j]=a[cnt];
            cnt*=2;
            int h=1;t=0; p[h]=(re){0,0};
            rep(i,1,cnt)
            {
                while (h<=t&&(i-p[h].a)>(len/2)) h++;
                maxa(ans,i+a[i]+p[h].b);
                while (h<=t&&a[i]-i>=p[t].b) t--;
                p[++t]=(re){i,a[i]-i};
            }
        }
        if (!dfn[v])
        { 
          dfs(v,x); low[x]=MIN(low[x],low[v]);
        }
        if (low[v]>dfn[x])
        {
            if (f[v][0]>f[x][0])
            {
                f[x][1]=f[x][0]; f[x][0]=f[v][0];
            } else
            if (f[v][0]>f[x][1]) f[x][1]=f[v][0];
        }
        mina(low[x],dfn[v]);
    }
    maxa(ans,f[x][0]+f[x][1]+2);
    f[x][0]++;
}
int main()
{
    freopen("1.in","r",stdin);
    freopen("1.out","w",stdout);
    while (cin>>n>>m)
    {
    ans=0; me(head); l=0; me(low); me(dfn); me(f); 
    rep(i,1,m)
    {
        int k,x,y;
        cin>>k>>x;
        rep(j,1,k-1)
        {
            cin>>y;
            arr(x,y); arr(y,x);
            x=y;
        }
    }
    dfs(1,0);
    cout<<ans<<endl;
    } 
    return 0; 
}

 

3.求仙人掌两点间最短路

这个需要建立出真的圆方树

连边的时候保证了dfn最小的点在方点的上面,而其他点在方点的下面

定义圆点与方点间的距离为当前点到dfn最小点的距离

然后查最短路的时候

如果lca是圆点,那么就是他们之间距离

如果lca是方点,那么说明是两边儿子(这个可以倍增找)在环上距离+他们到两边的距离

查找环上两点距离我用的是map维护 $M[i][j]$ 在第i个环上j的前缀和

#我这个代码在有重边的时候是错的,因为要判父亲的边表而不是点。。(虽然数据并没有重边)

稍微改一下就可以了

 

#include <bits/stdc++.h>
using namespace std;
#define rint register int
#define IL inline
#define rep(i,h,t) for(int i=h;i<=t;i++)
#define dep(i,t,h) for(int i=t;i>=h;i--)
#define ll long long
#define me(x) memset(x,0,sizeof(x))
#define mp make_pair
namespace IO{
    char ss[1<<24],*A=ss,*B=ss;
    IL char gc()
    {
        return A==B&&(B=(A=ss)+fread(ss,1,1<<24,stdin),A==B)?EOF:*A++;
    }
    template<class T> void read(T &x)
    {
        rint f=1,c; while (c=gc(),c<48||c>57) if (c=='-') f=-1; x=(c^48);
        while (c=gc(),c>47&&c<58) x=(x<<3)+(x<<1)+(c^48); x*=f; 
    }
    char sr[1<<24],z[20]; int Z,C1=-1;
    template<class T>void wer(T x)
    {
        if (x<0) sr[++C1]='-',x=-x;
        while (z[++Z]=x%10+48,x/=10);
        while (sr[++C1]=z[Z],--Z);
    }
    IL void wer1()
    {
        sr[++C1]=' ';
    }
    IL void wer2()
    {
        sr[++C1]='\n';
    }
    template<class T>IL void maxa(T &x,T y) {if (x<y) x=y;}
    template<class T>IL void mina(T &x,T y) {if (x>y) x=y;} 
    template<class T>IL T MAX(T x,T y){return x>y?x:y;}
    template<class T>IL T MIN(T x,T y){return x<y?x:y;}
};
using namespace IO;
const int N=1e5;
int n,m,q,head[N],l,cnt,color_cnt,fa[N],fa2[N],dfn[N],low[N];
ll dis[N];
struct re{
    int a,b,c,from;
    bool t;
}e[N*2];
struct re2{
    int a;
    ll b;
};
map<pair<int,int>,ll> M;
struct s2{
  int bz[21][N],dep[N],l,head[N];
  ll p[N],bz2[21][N];
  re e[N*2];
  void ycl()
  {
      rep(i,1,20)
        rep(j,1,n+color_cnt)
          bz[i][j]=bz[i-1][bz[i-1][j]],
        bz2[i][j]=bz2[i-1][j]+bz2[i-1][bz[i-1][j]];
  }
  IL void arr2(int x,int y,int z) 
  {
      e[++l].a=head[x];
      e[l].b=y;
      e[l].c=z;
      head[x]=l;
  }
  void dfs(int x,int y)
  {
      dep[x]=dep[y]+1;
      for (rint u=head[x];u;u=e[u].a)
      {
          int v=e[u].b;
          if (v!=y)
        {
          bz[0][v]=x; bz2[0][v]=e[u].c; 
          dfs(v,x);
        }
      }
  }
  re2 lca(int x,int y)
  {
      if (dep[x]<dep[y]) swap(x,y);
    ll ans=0; 
      dep(i,20,0)
        if (dep[bz[i][x]]>=dep[y]) ans+=bz2[i][x],x=bz[i][x];
      if (x==y) return((re2){x,ans});
    dep(i,20,0)
      if (bz[i][x]!=bz[i][y]) 
        ans+=bz2[i][x]+bz2[i][y],x=bz[i][x],y=bz[i][y];
    ans+=bz2[0][x]+bz2[0][y];
    return (re2){bz[0][x],ans};   
  }
  re2 jump(int x,int y)
  {
      if (dep[x]==y) return((re2){x,0});
      ll ans=0;
      dep(i,20,0)
        if (dep[bz[i][x]]>y) ans+=bz2[i][x],x=bz[i][x];
      ans+=bz2[0][x],x=bz[0][x];
      return (re2){x,ans};
  }
  int query(int x,int y)
  {
      re2 k=lca(x,y);
      if (k.a<=n) return(k.b);
      ll ans=0;
      re2 k1=jump(x,dep[k.a]+1); re2 k2=jump(y,dep[k.a]+1);
      ans=k1.b+k2.b;
    ll len1=abs(M[mp(k.a,k1.a)]-M[mp(k.a,k2.a)]);
    len1=MIN(len1,p[k.a]-len1);
    return ans+len1;
  }
}S;
IL void arr(int x,int y,int z)
{
    e[++l].a=head[x];
    e[l].from=x;
    e[l].b=y;
    e[l].c=z;
    head[x]=l;
}
void dfs(int x,int k,int y)
{
    dfn[x]=low[x]=++cnt; fa[x]=y; fa2[x]=k;
    for (rint u=head[x];u;u=e[u].a)
    {
        int v=e[u].b;
        if (v==y) continue;
        if (!dfn[v])
        {
            dis[v]=dis[x]+e[u].c;
            dfs(v,u,x); mina(low[x],low[v]);
        }
        if (dfn[v]<dfn[x])
        {
            color_cnt++;
            ll len1=0,len2=0;
            for(int j=x;j!=v;j=fa[j]) len2+=e[fa2[j]].c;
            len1=e[u].c;
            S.p[color_cnt]=len1+len2;
            for (int j=x;j!=v;j=fa[j])
            {
              M[mp(color_cnt,j)]=len1; 
              S.arr2(color_cnt,j,MIN(len1,len2)),
              S.arr2(j,color_cnt,MIN(len1,len2));
              e[fa2[j]].t=1;
              if (fa2[j]%2==1) e[fa2[j]+1].t=1;
              else e[fa2[j]-1].t=1;
              len1+=e[fa2[j]].c; len2-=e[fa2[j]].c;
            }
            e[u].t=1;
            if (u%2==1) e[u+1].t=1; else e[u-1].t=1;
            S.arr2(color_cnt,v,0),S.arr2(v,color_cnt,0);
        }
        mina(low[x],dfn[v]);
    }
}
int main()
{
    freopen("1.in","r",stdin);
    freopen("1.out","w",stdout);
    read(n); read(m); read(q);
    rep(i,1,m)
    {
        int x,y,z;
        read(x); read(y); read(z);
        arr(x,y,z); arr(y,x,z);
    }
    color_cnt=n;
    dfs(1,0,0);
    rep(i,1,l) if (!e[i].t) S.arr2(e[i].from,e[i].b,e[i].c);
    S.dfs(1,0);
    S.ycl();
    rep(i,1,q)
    {
        int x,y;
        read(x); read(y);
        wer(S.query(x,y)); wer2(); 
    }
    fwrite(sr,1,C1+1,stdout);
    return 0;
}

 

数据生成器

#include <bits/stdc++.h>
using namespace std;
#define rint register int
#define IL inline
#define rep(i,h,t) for (int i=h;i<=t;i++)
#define dep(i,t,h) for (int i=t;i>=h;i--)
#define me(x) memset(x,0,sizeof(x))
const int N=10000;
bool tf[N],now[N];
int fa[N];
bool pd(int x,int y)
{
    if (fa[x]==y||fa[y]==x||x==y) return(0);
    me(tf); tf[0]=1;
    int i;
    for (i=x;i;i=fa[i])
      tf[i]=1;
    for (i=y;tf[i]!=1;i=fa[i])
      if (now[i]) return(0);
    for (int j=x;j!=i;j=fa[j])
      if (now[j]) return(0);
    return(1);
}
void cl(int x,int y)
{
    me(tf); tf[0]=1;
    int i;
    for (i=x;i;i=fa[i])
      tf[i]=1;
    for (i=y;tf[i]!=1;i=fa[i])
      now[i]=1;
    for (int j=x;j!=i;j=fa[j])
      now[j]=1;
}
int main()
{
    freopen("1.in","w",stdout);
    srand(time(0)^size_t(new char));
    int n=30,m=32,q=100;
    cout<<n<<" "<<m<<" "<<q<<endl; 
    rep(i,2,n)
    {
        int x=rand()%(i-1)+1;
        int z=rand()%10+1;
        fa[i]=x;
        cout<<x<<" "<<i<<" "<<z<<endl;
    }
    rep(i,1,m-n+1)
    {
        int x=rand()%n+1,y=rand()%n+1;
        int z=rand()%10+1;
        while (!pd(x,y))
        {
            x=rand()%n+1,y=rand()%n+1;
        }
        cl(x,y);
        cout<<x<<" "<<y<<" "<<z<<endl;
    }
    rep(i,1,q)
    {
        int x=rand()%n+1,y=rand()%n+1;
        cout<<x<<" "<<y<<endl;
    }
    return 0;
}

 广义圆方树

posted @ 2018-11-30 09:13  尹吴潇  阅读(191)  评论(0编辑  收藏  举报