zoukankan      html  css  js  c++  java
  • 洛谷4630APIO2018铁人两项(圆方树+dp)

    QWQ神仙题啊(据说是今年第一次出现圆方树的地方)

    首先根据题目,我们就是求对于每一个路径((s,t))他的贡献就是两个点之间的点数,但是图上问题我并没有办法很好的解决。。。

    这时候考虑圆方树,我们将圆方树建出来之后,
    我们令方点的权值是他所连接的圆点之和,圆点的权值是(-1)
    这里之所以让圆点的贡献是-1,是为了方便表示路径的贡献(不然貌似比较复杂)。

    如果我们这么赋值的话,那么一个条路经的贡献就应该是点权之和。

    QWQ可惜枚举两个端点是(O(n^2))复杂度的

    那么这时候,我们就可以直接考虑每个点作为中心的贡献,那么他的贡献就应该是:

    子树外到子树内的贡献+子树之间的贡献。

    那么我们只需要一边(dfs),一边维护(size)并更新(ans)就行

    void dfs(int x)
    {
     vis[x]=1;
     int tmp=0;
     if (x<=n) tmp=1;
     for (int i=point[x];i;i=nxt[i])
     {
      int p = to[i];
      if (vis[p]) continue;
      dfs(p);
      ans=ans+tmp*size[p]*val[x];
      tmp+=size[p];
     // cout<<ans<<endl;
     }
     ans=ans+size[x]*(sum-size[x])*val[x];
    }
    

    不过要注意的是,最后的(ans)需要乘2,因为是双向的

    而且图不一定联通!!!!!

    // luogu-judger-enable-o2
    #include<iostream>
    #include<cstdio>
    #include<algorithm>
    #include<cstring>
    #include<cmath>
    #include<queue>
    #include<map>
    #include<set>
    #define mk makr_pair
    #define ll long long
    #define int long long
    using namespace std;
    inline int read()
    {
      int x=0,f=1;char ch=getchar();
      while (!isdigit(ch)) {if (ch=='-') f=-1;ch=getchar();}
      while (isdigit(ch)) {x=(x<<1)+(x<<3)+ch-'0';ch=getchar();}
      return x*f;
    }
    const int maxn = 3e5+1e2;
    const int maxm = 2*maxn;
    int point[maxn],nxt[maxm],to[maxm];
    int point1[maxn],nxt1[maxm],to1[maxm];
    int cnt,cnt1;
    int n,m;
    int f[maxn],val[maxn],size[maxn],g[maxn];
    int vis[maxn];
    int top,st[maxn];
    int low[maxn],dfn[maxn];
    int ans;
    void addedge(int x,int y)
    {
     nxt[++cnt]=point[x];
     to[cnt]=y;
     point[x]=cnt;
    }
    void addedge1(int x,int y)
    {
     nxt1[++cnt1]=point1[x];
     to1[cnt1]=y;
     point1[x]=cnt1;
    } 
    int tot,num;
    void tarjan(int x,int fa)
    {
     dfn[x]=low[x]=++tot;
     st[++top]=x;
     for (int i=point1[x];i;i=nxt1[i])
     {
      int p = to1[i];
      if (p==fa) continue;
         if (!dfn[p])
         {
          tarjan(p,x);
          low[x]=min(low[x],low[p]);
          if (low[p]>=dfn[x])
       {
           ++num;
           addedge(num,x);
           addedge(x,num);
           val[num]++;
           do{
            addedge(st[top],num);
            addedge(num,st[top]);
            val[num]++;
            top--;
        }while (st[top+1]!=p);
       }
      }
      else
        low[x]=min(low[x],dfn[p]);
     }
    }
    void dp(int x,int faa)
    {
     if (x<=n)
       size[x]=1;
     for (int i=point[x];i;i=nxt[i])
     {
      int p = to[i];
      if (p==faa) continue;
      dp(p,x);
      size[x]+=size[p];
     }
     //cout<<x<<" "<<size[x]<<endl;
    }
    int sum;
    void dfs(int x)
    {
     vis[x]=1;
     int tmp=0;
     if (x<=n) tmp=1;
     for (int i=point[x];i;i=nxt[i])
     {
      int p = to[i];
      if (vis[p]) continue;
      dfs(p);
      ans=ans+tmp*size[p]*val[x];
      tmp+=size[p];
     // cout<<ans<<endl;
     }
     ans=ans+size[x]*(sum-size[x])*val[x];
    }
    signed main()
    {
      n=read(),m=read();
      num=n;
      for (int i=1;i<=n;i++) val[i]=-1;
      for (int i=1;i<=m;i++)
      {
       int x=read(),y=read();
       addedge1(x,y);
       addedge1(y,x); 
      }
      for (int i=1;i<=n;i++)
      {
       if(!dfn[i]) tarjan(i,0);
      }
      for (int i=1;i<=n;i++)
      {
       if(!vis[i])
       {
        dp(i,0);
        sum=size[i];
        dfs(i);
     }
      }
      cout<<ans*2<<endl;
      return 0;
    }
    
  • 相关阅读:
    第11组 团队Git现场编程实战
    第11组 团队项目-需求分析报告
    团队项目-选题报告
    第二次结对编程作业
    第11组 团队展示
    第一次结对编程作业
    Nginx学习笔记
    Git学习笔记
    Qt学习笔记
    Eclipse中Outline里各种图标的含义
  • 原文地址:https://www.cnblogs.com/yimmortal/p/10161945.html
Copyright © 2011-2022 走看看