• 圆方树


    当一张图每条边最多属于一个环的时候,这个图叫做仙人掌

    圆方树用来解决仙人掌上的问题

    1.仙人掌上最大独立集

    仙人掌上做dp可以分成树边和环边两种边

    所以相当于实现树形和环形dp

      void tarjan(ll x,ll y)
      {
        dfn[x]=low[x]=++cnt;
        for (ll u=head[x];u;u=e[u].a)
        {
            ll v=e[u].b;
            if (v==y) continue;
            if (!dfn[v])
            {
                fa[v]=x;
                tarjan(v,x);
                low[x]=min(low[x],low[v]);
            } else low[x]=min(low[x],dfn[v]);
            if (low[v]>dfn[x]) {树形dp;} 
        }
        for (ll u=head[x];u;u=e[u].a)
        {
            ll v=e[u].b;
            if (fa[v]!=x&&dfn[v]>dfn[x]) 环形dp;
        }
      }

    圆方树dp就套上面的板子就可以了

    //#pragma GCC optimize("Ofast,no-stack-protector,unroll-loops,fast-math")
    //#pragma GCC target("sse,sse2,sse3,ssse3,sse4.1,sse4.2,avx,avx2,popcnt,tune=native")
    
    //#include <immllrin.h>
    //#include <emmllrin.h>
    #include <bits/stdc++.h>
    using namespace std;
    #define rep(i,h,t) for (ll i=h;i<=t;i++)
    #define dep(i,t,h) for (ll i=t;i>=h;i--)
    #define ll long long
    #define me(x) memset(x,0,sizeof(x))
    #define IL inline
    #define rll register ll
    inline ll rd(){
        ll x=0;char c=getchar();bool f=0;
        while(!isdigit(c)){if(c=='-')f=1;c=getchar();}
        while(isdigit(c)){x=(x<<1)+(x<<3)+(c^48);c=getchar();}
        return f?-x:x;
    }
    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 maxa(T &x,T y)
    {
        if (y>x) x=y;
    }
    template<class T>void mina(T &x,T y)
    {
        if (y<x) x=y;
    }
    template<class T>void read(T &x)
    {
        ll 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*10+(c^48); x*=f;
    }
    struct cp {
        ll x,y;
        cp operator +(cp B)
        {
            return (cp){x+B.x,y+B.y};
        }
        cp operator -(cp B)
        {
            return (cp){x-B.x,y-B.y};
        }
        ll operator *(cp B)
        {
            return x*B.y-y*B.x;
        }
        ll half() { return y < 0 || (y == 0 && x < 0); }
    };
    struct re{
        ll a,b,c;
    };
    const ll N=3e5;
    ll n,m,q,f1,f2;
    ll dfn[N],low[N],cnt,fa[N],f[N][2];
    ll head[N],l;
    re e[N*2];
    void arr(ll x,ll y)
    {
      e[++l].a=head[x];
      e[l].b=y;
      head[x]=l;
    }
    void solve(ll x,ll v)
    { 
      ll f0=0,f1=-1e9,t0,t1;
      for (ll i=v;i!=x;i=fa[i])
      {
          t0=f[i][0]+max(f0,f1);
          t1=f[i][1]+f0;
          f0=t0; f1=t1;
      }
      f[x][0]+=max(f0,f1);
      f0=-1e9,f1=0;
      for (ll i=v;i!=x;i=fa[i])
      {
          t0=f[i][0]+max(f0,f1);
          t1=f[i][1]+f0;
          f0=t0; f1=t1;
      }
      f[x][1]+=f0;
    }
      void tarjan(ll x,ll y)
      {
        dfn[x]=low[x]=++cnt;
    //    cerr<<x<<endl;
        f[x][0]=0; f[x][1]=1;
        for (ll u=head[x];u;u=e[u].a)
        {
            ll v=e[u].b;
            if (v==y) continue;
            if (!dfn[v])
            {
                fa[v]=x;
                tarjan(v,x);
                low[x]=min(low[x],low[v]);
            } else low[x]=min(low[x],dfn[v]);
            if (low[v]>dfn[x]) {f[x][0]+=max(f[v][1],f[v][0]),f[x][1]+=f[v][0];} 
        }
        for (ll u=head[x];u;u=e[u].a)
        {
            ll v=e[u].b;
            if (fa[v]!=x&&dfn[v]>dfn[x]) solve(x,v);
        }
      }
    int main()
    {
       ios::sync_with_stdio(false);
       cin>>n>>m;
       rep(i,1,m)
       {
            ll u,v,w;
            cin>>u>>v;
            arr(u,v); arr(v,u);
       }  
       tarjan(1,0);
       cout<<max(f[1][0],f[1][1]);
       return 0;
    }
    View Code

    2.Winter Festival

    题目描述

    Peter最喜欢的季节是冬天。为了庆祝冬天的活动,Peter准备装饰他所在的城市。

    城市有许多区域,有一些道路连接着某些区域对。Peter想给每条道路装饰成数字0,12之一。

    一组方案合法当且仅当满足以下两个条件:

    1. 对于任意两条不同的边(x, y), (x, z),它们的边权和mod 3不等于1。

    2. 对于任意一个简单环,里面的所有边的边权和必须是奇数。

    请找到一组合法方案,使得所有边的边权之和最小。(n,m<=100000)

     题解:

    比较显然对于边权和mod 3不等于1这个条件 等价于不能同时出现0,1且不能出现两个2

    于是如果这个是树我们可以记录f[x][i][j][k]表示当前在x点0/1/2有无出现

    现在变成仙人掌多一个环形dp

    代码好像写的比较麻烦

    //#pragma GCC optimize("Ofast,no-stack-protector,unroll-loops,fast-math")
    //#pragma GCC target("sse,sse2,sse3,ssse3,sse4.1,sse4.2,avx,avx2,popcnt,tune=native")
    
    //#include <immllrin.h>
    //#include <emmllrin.h>
    #include <bits/stdc++.h>
    using namespace std;
    #define rep(i,h,t) for (ll i=h;i<=t;i++)
    #define dep(i,t,h) for (ll i=t;i>=h;i--)
    #define ll long long
    #define me(x) memset(x,0,sizeof(x))
    #define IL inline
    #define rll register ll
    #define me1(g) memset(g,1,sizeof(g))
    #define mep(x,y) memcpy(x,y,sizeof(y))
    inline ll rd(){
        ll x=0;char c=getchar();bool f=0;
        while(!isdigit(c)){if(c=='-')f=1;c=getchar();}
        while(isdigit(c)){x=(x<<1)+(x<<3)+(c^48);c=getchar();}
        return f?-x:x;
    }
    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 maxa(T &x,T y)
    {
        if (y>x) x=y;
    }
    template<class T>void mina(T &x,T y)
    {
        if (y<x) x=y;
    }
    template<class T>void read(T &x)
    {
        ll 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*10+(c^48); x*=f;
    }
    struct cp {
        ll x,y;
        cp operator +(cp B)
        {
            return (cp){x+B.x,y+B.y};
        }
        cp operator -(cp B)
        {
            return (cp){x-B.x,y-B.y};
        }
        ll operator *(cp B)
        {
            return x*B.y-y*B.x;
        }
        ll half() { return y < 0 || (y == 0 && x < 0); }
    };
    struct re{
        ll a,b,c;
    };
    const ll N=3e5;
    ll n,m,q,f1,f2;
    ll dfn[N],low[N],cnt,fa[N],f[N][2][2][2],cnt1[N];
    ll head[N],l,b[N];
    re e[N*2];
    void arr(ll x,ll y)
    {
      e[++l].a=head[x];
      e[l].b=y;
      head[x]=l;
    }
    bool tt=0;
    void cl(int x)
    {
        if (cnt1[x]==0) cnt1[x]++;
        else tt=1;
    }
    void mina(int &x,int y)
    {
        if (y<x) x=y;
    }
    void solve(ll x,ll v,int k)
    {
      for (ll i=v;i!=x;i=fa[i])
      {
          cl(b[i]); cl(b[i]^1);
      }
      cl(k); cl(k^1);
      ll g[2][2][2][2][3];
      me1(g);
      rep(i1,0,1)
        rep(j1,0,1)
          rep(k1,0,1)
            rep(t,0,2)
            {
              bool t1=i1|(t==0);
              bool t2=j1|(t==1);
              int t3=k1+(t==2);
              if (t1&t2||t3>=2) continue;
              g[t1][t2][t3][t%2][t]=min(g[t1][t2][t3][t%2][t],f[v][i1][j1][k1]+t);
            }
      for (ll i=fa[v];i!=fa[x];i=fa[i])
      {
          ll h[2][2][2][2][3]; me1(h); 
          rep(i1,0,1)
          rep(j1,0,1)
            rep(k1,0,1)
              rep(i2,0,1)
                rep(j2,0,1)
                  rep(k2,0,1)
                    rep(t,0,2)
                    { 
                      bool t1=i2|(t==0);
                      bool t2=j2|(t==1);
                      int t3=k2+(t==2);
                      if ((t1&t2)||t3>=2) continue;
                      t1=i1|(t==0);
                      t2=j1|(t==1);
                      t3=k1+(t==2);
                      if ((t1&t2)||t3>=2) continue;
                      rep(p1,0,1)
                         rep(p2,0,2)
                           mina(h[t1][t2][t3][(p1+t)%2][p2],f[i][i1][j1][k1]+g[i2][j2][k2][p1][p2]+t);
                     }
         mep(g,h); 
      }
      me1(f[x]);
      rep(i1,0,1)
        rep(j1,0,1)
          rep(k1,0,1)
            rep(p1,1,1)
              rep(t,0,2)
              {
                   bool t1=i1|(t==0);
                 bool t2=j1|(t==1);
                 int t3=k1+(t==2);
                 if (t1&t2||t3>=2) continue;
                 mina(f[x][t1][t2][t3],g[i1][j1][k1][p1][t]);
              }
    }
    void tarjan(ll x,ll y)
    {
      if (tt) return;
      dfn[x]=low[x]=++cnt;
      for (ll u=head[x];u;u=e[u].a)
      {
        ll v=e[u].b;
        if (v==y) continue;
        if (!dfn[v])
        {
            fa[v]=x; b[v]=u;
            tarjan(v,x);
            low[x]=min(low[x],low[v]);
        } else low[x]=min(low[x],dfn[v]);
        if (low[v]>dfn[x]) 
        {
            ll g[2][2][2];
            me1(g);
            rep(i1,0,1)
              rep(j1,0,1)
                rep(k1,0,1)
                  rep(i2,0,1)
                    rep(j2,0,1)
                      rep(k2,0,1)
                        rep(t,0,2)
                        {
                            bool t1=i2|(t==0);
                            bool t2=j2|(t==1);
                            int t3=k2+(t==2);
                            if ((t1&t2)||t3>=2) continue;
                            t1=i1|(t==0);
                            t2=j1|(t==1);
                            t3=k1+(t==2);
                            if ((t1&t2)||t3>=2) continue;
                            g[t1][t2][t3]=min(g[t1][t2][t3],f[x][i1][j1][k1]+f[v][i2][j2][k2]+t);
                        }
            mep(f[x],g);
        } 
      }
      for (ll u=head[x];u;u=e[u].a)   {
            ll v=e[u].b;
            if (fa[v]!=x&&dfn[v]>dfn[x]) solve(x,v,u);
        }
    }
    int main()
    {
       ios::sync_with_stdio(false);
       l=1;
       cin>>n>>m;
       rep(i,1,m)
       {
            ll u,v;
            cin>>u>>v;
            arr(u,v); arr(v,u);
       }
       ll ans=0;
       rep(i,1,n)
       if (!dfn[i])
       {  
          tarjan(i,0);
          vector<int> ve;
          rep(i1,0,1)
            rep(j1,0,1)
              rep(k1,0,1)
                ve.push_back(f[i][i1][j1][k1]);
          sort(ve.begin(),ve.end());
          ans+=ve[0]; 
       }
       if (tt||ans>2*m) cout<<-1<<endl;
       else
       {
           cout<<ans<<endl;
       }
       return 0;
    }
    View Code

    3.仙人掌上最短路

    这个题一定要建出圆方树

    圆方树的建立方法是对于树边直接连,对于环边把环边都连到一个新点上

    定义老点叫做圆点新点叫做方点

    圆点到圆点的距离就是原树距离

    圆点到方点的距离就是圆点到环上dfs序最小点的环上最短距离

    求两个点距离时,先求出lca=k

    若k是圆点,距离就是lca[x]+lca[y]-2*lca[k]

    若k是方点,距离就是lca[x]+lca[y]-lca[a]-lca[b]+dis(a,b)

    其中a,b分别代表x,y到k差一步的点,dis(a,b)代表a,b在环上的距离

    正确性比较容易证明

    //#pragma GCC optimize("Ofast,no-stack-protector,unroll-loops,fast-math")
    //#pragma GCC target("sse,sse2,sse3,ssse3,sse4.1,sse4.2,avx,avx2,popcnt,tune=native")
    
    //#include <immllrin.h>
    //#include <emmllrin.h>
    #include <bits/stdc++.h>
    using namespace std;
    #define rep(i,h,t) for (ll i=h;i<=t;i++)
    #define dep(i,t,h) for (ll i=t;i>=h;i--)
    #define ll long long
    #define me(x) memset(x,0,sizeof(x))
    #define IL inline
    #define rll register ll
    inline ll rd(){
        ll x=0;char c=getchar();bool f=0;
        while(!isdigit(c)){if(c=='-')f=1;c=getchar();}
        while(isdigit(c)){x=(x<<1)+(x<<3)+(c^48);c=getchar();}
        return f?-x:x;
    }
    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 maxa(T &x,T y)
    {
        if (y>x) x=y;
    }
    template<class T>void mina(T &x,T y)
    {
        if (y<x) x=y;
    }
    template<class T>void read(T &x)
    {
        ll 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*10+(c^48); x*=f;
    }
    struct cp {
        ll x,y;
        cp operator +(cp B)
        {
            return (cp){x+B.x,y+B.y};
        }
        cp operator -(cp B)
        {
            return (cp){x-B.x,y-B.y};
        }
        ll operator *(cp B)
        {
            return x*B.y-y*B.x;
        }
        ll half() { return y < 0 || (y == 0 && x < 0); }
    };
    struct re{
        ll a,b,c;
    };
    const ll N=3e5;
    ll n,m,q,f1,f2;
    ll dis[N],s[N],b[N];
    ll id[N];
    struct yy{
        re e[N*2];
        ll head[N],d[N]; 
        ll bz[20][N],l;
        void arr(ll x,ll y,ll z)
        { 
            e[++l].a=head[x];
            e[l].b=y;
            e[l].c=z;
      //      if (x<y) cerr<<x<<" "<<y<<endl;
            head[x]=l;
        }
        void dfs(ll x,ll y)
        {
           bz[0][x]=y;
           rep(i,1,19) bz[i][x]=bz[i-1][bz[i-1][x]];
           for (ll u=head[x];u;u=e[u].a)
           {
                ll v=e[u].b;
                if (v==y) continue;
                dis[v]=dis[x]+e[u].c;
                d[v]=d[x]+1;
                dfs(v,x);
           }
        }
        ll lca(ll x,ll y)
        {
            if (d[x]<d[y]) swap(x,y);
            dep(i,19,0) if (d[bz[i][x]]>=d[y]) x=bz[i][x];
            if (x==y) return x;
            dep(i,19,0) if (bz[i][x]!=bz[i][y]) x=bz[i][x],y=bz[i][y];
            f1=x; f2=y;
            return bz[0][x];
        }
    }G2;
    struct xx{
      ll dfn[N],low[N],cnt,fa[N],n;
      ll head[N],l;
      re e[N*2];
      void arr(ll x,ll y,ll z)
      {
          e[++l].a=head[x];
          e[l].b=y;
          e[l].c=z;
          head[x]=l;
      }
      void solve(ll x,ll v,ll w)
      { 
         n++;
         ll now=w,now2=0;
         for (ll i=v;i!=fa[x];i=fa[i])
         {
             s[i]=now;
             now+=b[i];
             id[i]=now2++;
         }
         s[n]=s[x]; s[x]=0;
         for (ll i=v;i!=fa[x];i=fa[i]) G2.arr(n,i,min(s[i],s[n]-s[i])),G2.arr(i,n,min(s[i],s[n]-s[i]));
      }
      void tarjan(ll x,ll y)
      {
        dfn[x]=low[x]=++cnt; 
        for (ll u=head[x];u;u=e[u].a)
        {
            ll v=e[u].b;
            if (v==y) continue;
            if (!dfn[v])
            {
                fa[v]=x; b[v]=e[u].c;
                tarjan(v,x);
                low[x]=min(low[x],low[v]);
            } else low[x]=min(low[x],dfn[v]);
            if (low[v]>dfn[x]) {G2.arr(x,v,e[u].c); G2.arr(v,x,e[u].c);} 
        }
        for (ll u=head[x];u;u=e[u].a)
        {
            ll v=e[u].b;
            if (fa[v]!=x&&dfn[v]>dfn[x]) solve(x,v,e[u].c);
        }
      }
    }G1;
    int main()
    {
       ios::sync_with_stdio(false);
       cin>>n>>m>>q;
       rep(i,1,m)
       {
            ll u,v,w;
            cin>>u>>v>>w;
            G1.arr(u,v,w); G1.arr(v,u,w);
       } 
       G1.n=n;
       G1.tarjan(1,0);
       G2.dfs(1,0);
       rep(i,1,q)
       {
             ll u,v;
             cin>>u>>v;
             ll g=G2.lca(u,v);
             if (g<=n) cout<<dis[u]+dis[v]-2*dis[g]<<endl;
             else
             {
                 ll ans=dis[u]+dis[v]-dis[f1]-dis[f2];
                 if (id[f1]<=id[f2]) ans+=min(s[f1]+s[g]-s[f2],s[f2]-s[f1]);
                 else ans+=min(s[f2]+s[g]-s[f1],s[f1]-s[f2]);;
                 cout<<ans<<endl;
             }
       }
       return 0;
    }
    View Code
  • 相关阅读:
    QT常用技巧--程序延时
    python中zip()函数的用法
    numpy.random.choice(a, size = None, replace = True, p = None)用法
    Python keras.layers .ZeroPadding2D() 详解
    Socat 入门笔记
    echo命令的使用
    Type Error('keyword argument not understood: ', 'interpolation') 解决方案
    Pytorch 包下载
    双边滤波Matlab代码
    hihocoder 第170周 Word Construction (dfs+剪枝)
  • 原文地址:https://www.cnblogs.com/yinwuxiao/p/15000155.html
Copyright © 2020-2023  润新知