• 三元环HDU 6184


    HDU - 6184  C - Counting Stars

      题目大意:有n个点,m条边,问有一共有多少个‘structure’也就是满足V=(A,B,C,D) and E=(AB,BC,CD,DA,AC)这样一个图形,类似于四边形中间连接了一条对角线。

      如果我们把这个四边形拆分的话,其实就是两个共用一条边的三角形,而在图中就是三元环。求三元环有两种求法,个人感觉这个三元环的时间复杂度很玄学。

      第一种一种就是枚举点x,然后枚举和点x相连的y,这时根据y的度,如果y的度小于等于sqrt(m),那么我们可以直接枚举和y相连的z,看z和x是否相连,这时时间复杂度最差是m*sqrt(m),如果y的度大于sqrt(m)的话,说明y有很多点相连,再枚举y的话就会超时,这时枚举再枚举x找z,根据set或者map来判断y和z是否相连,复杂度最差也是m*sqrt(m),详情见代码

     1 #include<cstdio>
     2 #include<vector>
     3 #include<set>
     4 #include<cstring> 
     5 using namespace std;
     6 typedef long long ll;
     7 const int N=100118;
     8 set<ll> s;
     9 vector<int> vv[N];
    10 int link[N],book[N];
    11 inline void init(int n)
    12 {
    13     s.clear();
    14     for(int i=0;i<=n;i++)
    15     {
    16         vv[i].clear();
    17         link[i]=0;
    18         book[i]=0;
    19     }
    20 }
    21 int main()
    22 {
    23     int n,m,u,v;
    24     while(~scanf("%d%d",&n,&m))
    25     {
    26         init(n);
    27         for(int i=0;i<m;i++)
    28         {
    29             scanf("%d%d",&u,&v);
    30             vv[u].push_back(v);
    31             vv[v].push_back(u);
    32             s.insert(1ll*u*(n+1)+v);//set保存u和v相连的信息 
    33             s.insert(1ll*v*(n+1)+u);
    34         }
    35         ll ans=0;
    36         for(int x=1;x<=n;x++)
    37         {
    38             book[x]=1;//每个点只有枚举一遍 
    39             for(int i=0;i<vv[x].size();i++)
    40                 link[vv[x][i]]=x;//记录和x点相连的点 
    41             //枚举和x相连的y 
    42             for(int i=0;i<vv[x].size();i++)
    43             {
    44                 int y=vv[x][i],sum=0;
    45                 if(book[y])
    46                     continue;
    47                 if(1ll*vv[y].size()*vv[y].size()<=m)
    48                 {
    49                     //枚举找到和x相连的z
    50                     for(int j=0;j<vv[y].size();j++)
    51                         if(link[vv[y][j]]==x)
    52                             sum++;
    53                 }
    54                 else
    55                 {
    56                     //枚举找到和y相连的z 
    57                     for(int j=0;j<vv[x].size();j++)
    58                         if(s.find(1ll*y*(n+1)+vv[x][j])!=s.end())
    59                             sum++;
    60                 }
    61                 ans+=1ll*sum*(sum-1)/2;//每两个三元环就可以组成一个需要的图形 
    62             }
    63         }
    64         printf("%lld
    ",ans);
    65     }
    66     return 0;
    67 }
    暴力m*sqrt(m)

      当然上面那个做法有点类似于暴力,而且前向星建图会超时,不知道为啥子,所以我们还需要第二种方法

      第二种是枚举边,不过得先对边进行处理。也就是统计每个点的度,然后我们根据这个度把之前的双向边变成度数大的点指向度数小的点,度数相同按序号的有向边,这样构成了一个有向无环图。这样我们枚举每条边,统计这条边能构成几个三元环,最后统计答案。但时间复杂度我不懂算,很玄学,比第一种快了不只一倍,并且在建边时用swap会超时。。

     1 #include<cstdio>
     2 #include<algorithm> 
     3 #include<vector> 
     4 using namespace std;
     5 typedef long long ll;
     6 typedef pair<int,int> pii;
     7 const int N=100118;
     8 vector<pii> vv[N];
     9 int link[N],line[N],du[N],val[2*N],u[2*N],v[2*N];
    10 inline void init(int n)
    11 {
    12     for(int i=0;i<=n;i++)
    13     {
    14         du[i]=0;
    15         vv[i].clear();
    16         link[i]=line[i]=0;
    17     }
    18 }
    19 int main()
    20 {
    21     int n,m;
    22     while(~scanf("%d%d",&n,&m))
    23     {
    24         init(n);
    25         for(int i=1;i<=m;i++)
    26         {
    27             val[i]=0;
    28             scanf("%d%d",&u[i],&v[i]);
    29             du[u[i]]++,du[v[i]]++;
    30         }
    31         for(int i=1;i<=m;i++)//按度数改成有向边 
    32         {//每条边记录下另一个端点和边的编号 
    33             if(du[u[i]]<du[v[i]])
    34                 vv[u[i]].push_back(pii(v[i],i));
    35             else if(du[u[i]]>du[v[i]])
    36                 vv[v[i]].push_back(pii(u[i],i));
    37             else 
    38             {
    39                 if(u[i]<v[i])
    40                     vv[u[i]].push_back(pii(v[i],i));
    41                 else
    42                     vv[v[i]].push_back(pii(u[i],i));
    43             }
    44         //    改成swap(u[i],v[i]),然后再建边会超时 
    45         }
    46         for(int i=1;i<=m;i++)//枚举每条边 
    47         {
    48             int x=u[i],y=v[i];
    49             for(int j=0;j<vv[x].size();j++)
    50             {//遍历和x相连的z 
    51                 int z=vv[x][j].first,id=vv[x][j].second;
    52                 link[z]=x;//z和x相连 
    53                 line[z]=id;//记录相连的这条边的编号 
    54             }
    55             for(int j=0;j<vv[y].size();j++)
    56             {//遍历和y相连的z 
    57                 int z=vv[y][j].first,id=vv[y][j].second;
    58                 if(link[z]==x)
    59                 {//如果这个z和x相连的话 
    60                     val[i]++;
    61                     val[id]++;
    62                     val[line[z]]++;    
    63                     //相关的三条边都可以组成了一个三元环 
    64                 }
    65             }
    66         }
    67         ll ans=0;
    68         for(int i=1;i<=m;i++)
    69             ans+=1ll*val[i]*(val[i]-1)/2;
    70         printf("%lld
    ",ans);
    71     }
    72     return 0;
    73 }
    vector建图
     1 #include<cstdio>
     2 #include<algorithm> 
     3 #include<cstring> 
     4 using namespace std;
     5 typedef long long ll;
     6 const int N=100118;
     7 struct Side{
     8     int v,ne;
     9 }S[2*N];
    10 int sn,head[N],link[N],line[N],du[N],val[2*N],u[2*N],v[2*N];
    11 inline void init(int n)
    12 {
    13     sn=0;
    14     for(int i=0;i<=n;i++)
    15     {
    16         head[i]=-1;
    17         du[i]=0;
    18         link[i]=0;
    19         line[i]=-1;
    20     }
    21 }
    22 inline void add(int u,int v)
    23 {
    24     S[sn].v=v;
    25     S[sn].ne=head[u];
    26     head[u]=sn++;
    27 }
    28 int main()
    29 {
    30     int n,m;
    31     while(~scanf("%d%d",&n,&m))
    32     {
    33         init(n);
    34         for(int i=0;i<m;i++)
    35         {
    36             val[i]=0;
    37             scanf("%d%d",&u[i],&v[i]);
    38             du[u[i]]++,du[v[i]]++;
    39         }
    40         for(int i=0;i<m;i++)
    41         {
    42             if(du[u[i]]<du[v[i]])
    43                 add(u[i],v[i]);
    44             else if(du[u[i]]>du[v[i]])
    45                 add(v[i],u[i]);
    46             else 
    47             {
    48                 if(u[i]<v[i])
    49                     add(u[i],v[i]);
    50                 else
    51                     add(v[i],u[i]);
    52             }
    53         }
    54         for(int i=0;i<m;i++)
    55         {
    56             int x=u[i],y=v[i];
    57             for(int j=head[x];j!=-1;j=S[j].ne)
    58             {
    59                 link[S[j].v]=x;
    60                 line[S[j].v]=j;
    61             }
    62             for(int j=head[y];j!=-1;j=S[j].ne)
    63             {
    64                 int z=S[j].v;
    65                 if(link[z]==x)
    66                 {
    67                     val[i]++;
    68                     val[j]++;
    69                     val[line[z]]++;    
    70                 }
    71             }
    72         }
    73         ll ans=0;
    74         for(int i=0;i<m;i++)
    75             ans+=1ll*val[i]*(val[i]-1)/2;
    76         printf("%lld
    ",ans);
    77     }
    78     return 0;
    79 }
    前向星建图
  • 相关阅读:
    接口和抽象的区别
    继承-子父类中成员方法特点
    基本类型和引用类型作为参数传递
    大三寒假生活6
    大三寒假生活5
    大三寒假生活4
    大三寒假生活3
    大三寒假生活2
    大三寒假生活指导
    ajax实现文本框的联想功能
  • 原文地址:https://www.cnblogs.com/LMCC1108/p/10693605.html
Copyright © 2020-2023  润新知