• LOJ 2587 「APIO2018」铁人两项——圆方树


    题目:https://loj.ac/problem/2587

    先写了 47 分暴力。

    对于 n<=50 的部分, n3 枚举三个点,把图的圆方树建出来,合法条件是 c 是 s -> f 路径上的方点连出去的某个圆点。像找 LCA 那样走一遍 s -> f 路径即可。

    对于树的部分,考虑一条路径对答案的贡献是其边数减 1 ,所以对于每条边求一下它在多少路径中,就是 siz[ v ] * ( n-siz[ v ] ) ( v 是它指向的点),然后答案再减去 ( C_n^2 ) 即可。

      注意答案还要乘 2 ,因为一条路径的贡献其实是两倍的 (边数 - 1),因为 s 和 f 位置可以互换。

    对于每个点度数最多是 2 的部分,是一些链和环。链就枚举路径的长度,可以算出有多少该长度路径以及贡献;环就考虑固定 s 的位置,对答案的贡献是一个等差数列,算一番即可。

    以为子任务 6 是基环树。写了个 n2 的。然后发现不是基环树而是仙人掌。(并且忘记考虑环的另一方向组成的路径了。懒得改了。)

    一定要好好判断什么情况是树的部分。因为是森林,所以不能写 if( m == n-1 ) 。

    #include<cstdio>
    #include<cstring>
    #include<algorithm>
    #define ll long long
    using namespace std;
    int rdn()
    {
      int ret=0;bool fx=1;char ch=getchar();
      while(ch>'9'||ch<'0'){if(ch=='-')fx=0;ch=getchar();}
      while(ch>='0'&&ch<='9')ret=ret*10+ch-'0',ch=getchar();
      return fx?ret:-ret;
    }
    int Mn(int a,int b){return a<b?a:b;}
    int Mx(int a,int b){return a>b?a:b;}
    const int N=1e5+5,M=4e5+5;
    int n,m,hd[N],xnt,to[M],nxt[M],rd[N];
    void add(int x,int y){to[++xnt]=y;nxt[xnt]=hd[x];hd[x]=xnt;rd[y]++;}
    namespace S1{
      int siz[N],tot;ll ans;
      bool vis[N];
      void ini_dfs(int cr,int fa)
      {
        siz[cr]=1; vis[cr]=1;
        for(int i=hd[cr],v;i;i=nxt[i])
          if((v=to[i])!=fa)
        ini_dfs(v,cr), siz[cr]+=siz[v];
      }
      void dfs(int cr,int fa)
      {
        for(int i=hd[cr],v;i;i=nxt[i])
          if((v=to[i])!=fa)
        {
          dfs(v,cr);
          ans+=(ll)siz[v]*(tot-siz[v]);
        }
      }
      void solve()
      {
        for(int i=1;i<=n;i++)
          if(!vis[i])
        {
          tot=0; ini_dfs(i,0); tot=siz[i];
          dfs(i,0); ans-=(ll)tot*(tot-1)/2;
        }
        printf("%lld
    ",ans*2);
      }
    }
    namespace S2{
      bool vis[N],flag; int cnt;
      void dfs(int cr,int fa)
      {
        vis[cr]=1; cnt++;
        for(int i=hd[cr],v;i;i=nxt[i])
          if((v=to[i])!=fa)
        {
          if(vis[v]){flag=1;return;}
          dfs(v,cr);
        }
      }
      void solve()
      {
        ll ans=0;
        for(int i=1;i<=n;i++)
          if(!vis[i])
        {
          flag=0;cnt=0;dfs(i,0);
          if(cnt<=2)continue;
          if(!flag)
            {
              for(int j=2;j<cnt;j++)
            {
              int ct=cnt-j;
              ans+=(ll)ct*(j-1)*2;//*2
            }
            }
          else ans+=(ll)(cnt-2)*(cnt-1)*cnt;
        }
        printf("%lld
    ",ans);
      }
    }
    namespace S3{
      const int N2=N<<1;
      int h2[N2],t2[M],nt2[M],dep[N2],cnt,col[N2],pre[N2];
      int dfn[N],low[N],tim,sta[N],top,tot;
      bool ins[N],vis[N2];
      void add(int x,int y)
      {
        t2[++xnt]=y;nt2[xnt]=h2[x];h2[x]=xnt;
      }
      void tarjan(int cr,int fa)
      {
        dfn[cr]=low[cr]=++tim;
        sta[++top]=cr; ins[cr]=1;
        for(int i=hd[cr],v;i;i=nxt[i])
          if((v=to[i])!=fa)
        {
          if(ins[v])low[cr]=Mn(low[cr],dfn[v]);
          else if(!dfn[v])
            {
              tarjan(v,cr);low[cr]=Mn(low[cr],low[v]);
              if(low[v]>=dfn[cr])
            {
              tot++; add(tot,cr); add(cr,tot);
              do{
                int tp=sta[top]; ins[tp]=0;
                add(tot,tp); add(tp,tot);
              }while(sta[top--]!=v);
            }
            }
        }
      }
      void dfs(int cr,int fa)
      {
        col[cr]=cnt;dep[cr]=dep[fa]+1;pre[cr]=fa;
        for(int i=h2[cr],v;i;i=nt2[i])
          if((v=t2[i])!=fa) dfs(v,cr);
      }
      bool chk(int s,int t,int c)
      {
        int x=s, y=t; if(dep[x]<dep[y])swap(x,y);
        while(dep[x]!=dep[y])
          {
        x=pre[x];if(x<=n)continue;
        for(int i=h2[x];i;i=nt2[i])
          if(t2[i]==c)return true;
          }
        while(x!=y)
          {
        x=pre[x]; y=pre[y];
        if(x>n)
          {
            for(int i=h2[x];i;i=nt2[i])
              if(t2[i]==c)return true;
          }
        if(y>n&&y!=x)
          {
            for(int i=h2[y];i;i=nt2[i])
              if(t2[i]==c)return true;
          }
          }
        return false;
      }
      void solve()
      {
        tot=n; xnt=0;
        for(int i=1;i<=n;i++)
          if(!dfn[i])top=0,tarjan(i,0);
        for(int i=1;i<=tot;i++)
          if(!col[i])cnt++,dfs(i,0);
        int ans=0;
        for(int s=1;s<=n;s++)
          for(int c=1;c<=n;c++)
        if(s!=c&&col[s]==col[c])
          for(int t=1;t<=n;t++)
            {
              if(t==s||t==c||col[t]!=col[s])continue;
              if(chk(s,t,c)) ans++;
            }
        printf("%d
    ",ans);
      }
    }
    namespace S4{
      const int N=1005;
      int tim,dfn[N],low[N],sta[N],top;
      bool vis[N],ins[N];
      int a[N],tot,dep[N]; ll ans,dp[N][N];
      void tarjan(int cr,int fa)
      {
        dfn[cr]=low[cr]=++tim;
        sta[++top]=cr; ins[cr]=1;
        for(int i=hd[cr],v;i;i=nxt[i])
          if((v=to[i])!=fa)
        {
          if(ins[v=to[i]])low[cr]=Mn(low[cr],dfn[v]);
          else if(!dfn[v])tarjan(v,cr),low[cr]=Mn(low[cr],low[v]);
        }
        if(dfn[cr]==low[cr])
          {
        if(sta[top]==cr){ins[cr]=0;top--;return;}
        do{
          int tp=sta[top]; a[++tot]=tp; vis[tp]=1; ins[tp]=0;
        }while(sta[top--]!=cr);
          }
      }
      void dfs(int cr,int fa)
      {
        dp[cr][0]=1;
        for(int i=hd[cr],v;i;i=nxt[i])
          if(!vis[v=to[i]]&&v!=fa)
        {
          dfs(v,cr);dep[cr]=Mx(dep[cr],dep[v]+1);
          for(int j=1;j<=dep[cr];j++)
            for(int k=0;k<=dep[v];k++)
              {
            ll tp=(ll)dp[cr][j]*dp[v][k];
            ans+=tp*(j+k);
              }
          for(int k=0;k<=dep[v];k++)
            dp[cr][k+1]+=dp[v][k];
        }
      }
      void solve()
      {
        for(int i=1;i<=n;i++)
          {
        if(dfn[i])continue;
            tot=tim=0;tarjan(i,0);
        if(!tot)
          {
            dfs(i,0);
            for(int j=2;j<=dep[i];j++)
              ans+=(ll)dp[i][j]*(j-1);
            continue;
          }
        for(int j=1;j<=tot;j++)dfs(a[j],0);
        for(int s=1;s<=tot;s++)
          for(int t=s+1;t<=tot;t++)
            for(int j=0;j<=dep[s];j++)
              for(int k=0;k<=dep[t];k++)
            {
              ll tp=(ll)dp[a[s]][j]*dp[a[t]][k];
              ans+=tp*(j+k+t-s-1);
            }
          }
        printf("%lld
    ",ans);
      }
    }
    bool vis[N],fg;
    void chk_dfs(int cr,int fa)
    {
      vis[cr]=1;
      for(int i=hd[cr],v;i;i=nxt[i])
        if((v=to[i])!=fa)
          {
        if(vis[v]){fg=1;return;}
        chk_dfs(v,cr); if(fg)return;
          }
    }
    int main()
    {
      n=rdn();m=rdn();
      for(int i=1,u,v;i<=m;i++)
        {
          u=rdn();v=rdn();add(u,v);add(v,u);
        }
      for(int i=1;i<=n;i++)
        if(!vis[i]){chk_dfs(i,0);if(fg)break;}
      if(!fg){S1::solve();return 0;}
      fg=0;
      for(int i=1;i<=n;i++)if(rd[i]>2){fg=1;break;}
      if(!fg){S2::solve();return 0;}
      if(n<=50){S3::solve();return 0;}
      if(n<=1000){S4::solve();return 0;}
      return 0;
    }
    View Code

     既然有了那个判断的想法,即一个 c 可行当且仅当它是 s -> f 路径上的方点连出去的某个圆点,那么就可以考虑怎样快速计算!

    比如对于一对 s , f ,贡献就是路径上方点连出去的圆点个数 - 2 。

    考虑每个点的贡献是多少,就能通过算该点在多少路径里而算出答案了。

    令方点权值是连出去的圆点个数,圆点权值是 -1 即可。考虑如果是端点的圆点,只和一个方点相邻,被算了一遍又自己减去一遍;如果是路径中的圆点,和两个方点相邻,被算了两遍又自己减去一遍,就正好。

    路径应该是两端是圆点的路径。计算方法见代码即可。

    #include<cstdio>
    #include<cstring>
    #include<algorithm>
    #define ll long long
    using namespace std;
    int rdn()
    {
      int ret=0;bool fx=1;char ch=getchar();
      while(ch>'9'||ch<'0'){if(ch=='-')fx=0;ch=getchar();}
      while(ch>='0'&&ch<='9')ret=ret*10+ch-'0',ch=getchar();
      return fx?ret:-ret;
    }
    int Mx(int a,int b){return a>b?a:b;}
    int Mn(int a,int b){return a<b?a:b;}
    const int N=2e5+5,M=4e5+5;
    int n,m,hd[N],xnt,to[M],nxt[M];
    int tim,dfn[N],low[N],sta[N],top; bool ins[N];
    int tot,tn,h2[N],t2[M],nt2[M],c[N],siz[N]; ll ans;
    void add(int x,int y){to[++xnt]=y;nxt[xnt]=hd[x];hd[x]=xnt;}
    void ad2(int x,int y){t2[++xnt]=y;nt2[xnt]=h2[x];h2[x]=xnt;}
    void tarjan(int cr,int fa)
    {
      dfn[cr]=low[cr]=++tim;
      sta[++top]=cr; ins[cr]=1; tot++;
      for(int i=hd[cr],v;i;i=nxt[i])
        if((v=to[i])!=fa)
          {
        if(ins[v])low[cr]=Mn(low[cr],dfn[v]);
        else if(!dfn[v])
          {
            tarjan(v,cr);low[cr]=Mn(low[cr],low[v]);
            if(low[v]>=dfn[cr])
              {
            tn++; ad2(cr,tn);ad2(tn,cr); c[tn]=1;
            do{
              int tp=sta[top]; ins[tp]=0;
              ad2(tp,tn); ad2(tn,tp); c[tn]++;
            }while(sta[top--]!=v);
              }
          }
          }
    }
    void dfs(int cr,int fa)
    {
      bool fg=(cr<=n); siz[cr]=fg; ll tp=0;
      for(int i=h2[cr],v;i;i=nt2[i])
        if((v=t2[i])!=fa)
          {
        dfs(v,cr); siz[cr]+=siz[v];
        tp+=(ll)siz[v]*(tot-siz[cr]);
          }
      if(fg)ans-=tp+tot-1; else ans+=c[cr]*tp;
    }
    int main()
    {
      n=rdn();m=rdn();
      for(int i=1,u,v;i<=m;i++)
        {
          u=rdn();v=rdn();add(u,v);add(v,u);
        }
      tn=n; xnt=0;
      for(int i=1;i<=n;i++)
        if(!dfn[i])
          {
        tim=tot=0;tarjan(i,0); dfs(i,0);
          }
      printf("%lld
    ",ans*2);
      return 0;
    }
  • 相关阅读:
    [转]Linux里的2>&1究竟是什么
    一段shell脚本分析
    [整理]Linux Crontab命令总结
    random seed()函数
    clear命令新认识
    泛型与发射初探,获取当前代码所在的行
    tomcat集群(转)
    查看本地电脑的端口及对应的使用程序
    信息摘要算法小试牛刀
    Linux非root用户安装jdk和tomcat(转)
  • 原文地址:https://www.cnblogs.com/Narh/p/10684899.html
Copyright © 2020-2023  润新知