[真正的单色三角形] BZOJ 3498 PA2009 Cakes

66 篇文章 0 订阅
35 篇文章 0 订阅

Description

给定一个无自环重边的无向图,求这个图的三元环的个数以及补图的三元环个数。
n105,m105

PS. 可以在这里

Analysis

这个东西 首先我们可以用BZOJ 2916 [Poi1997]Monochromatic Triangles的方法算出原图三角形和补图三角形的个数之和 然后我们再算出原图三角形的个数就好了
以下的代码都是求原图三角形的个数 复杂度 O(mm) 一般情况下跑得飞快

ps不知道哪里可以交啊写的代码没测过就放上来还说跑得快我也很不要脸啊

Solution1

首先我们可以考虑按阈值 m 把点分为两类
[奇葩方法]三元环题解

将所有点分成两类:度数 < sqrt(m)的和度数 > sqrt(m)的.
先求包含第一类点的三元环个数.
由于边很少,所以枚举2条边即可.由于一个点的度不超过sqrt(m),所以一条边最多被枚到(sqrt(m))次,最多枚M条边,所以这个操作时O(m*sqrt(m))的.
再求不包含第一类点的三元环个数.
由于每条边贡献2个度,所以二类点的数量是O(sqrt(m))级的.直接枚举三个点,复杂度O((sqrt(m))^3)=O(m*sqrt(m))
所以算法总的复杂度是O(m*sqrt(m))的.

#include<cstdio>
#include<cstdlib>
#include<algorithm>
#include<vector>
#include<cmath>
#define push_back pb
using namespace std;

inline char nc(){
  static char buf[100000],*p1=buf,*p2=buf;
  return p1==p2&&(p2=(p1=buf)+fread(buf,1,100000,stdin),p1==p2)?EOF:*p1++;
}
inline void read(int &x){
  char c=nc(),b=1;
  for (;!(c>='0' && c<='9');c=nc()) if (c=='-') b=-1;
  for (x=0;c>='0' && c<='9';x=x*10+c-'0',c=nc()); x*=b;
}

const int N=200005;

namespace HashMap{
  const int P=1000007,seed=2333;
  int u[N<<1],v[N<<1],next[N<<1];
  int head[P],inum;
  inline void add(int _u,int _v){
    int t=(_u*seed+_v)%P;
    u[++inum]=_u; v[inum]=_v; next[inum]=head[t]; head[t]=inum;
  }
  inline bool query(int _u,int _v){
    int t=(_u*seed+_v)%P;
    for (int p=head[t];p;p=next[p])
      if (u[p]==_u && v[p]==_v)
    return 1;
    return 0;
  }
}

struct edge{
  int u,v,next;
}G[N<<1];
int head[N],inum;
inline void add(int u,int v,int p){
  G[p].u=u; G[p].v=v; G[p].next=head[u]; head[u]=p;
}

int n,m;
int deg[N];
int s[N],pnt;
int Ans;
#define V1 G[p].v
#define V2 G[q].v
int main(){
  int iu,iv;
  freopen("t.in","r",stdin);
  freopen("t.out","w",stdout);
  read(n); read(m);
  for (int i=1;i<=m;i++){
    read(iu),read(iv);
    add(iu,iv,++inum); add(iv,iu,++inum);
    deg[iu]++,deg[iv]++;
    HashMap::add(iu,iv),HashMap::add(iv,iu);
  }
  int B=sqrt(m);
  for (int i=1;i<=n;i++)
    if (deg[i]<=B){
      for (int p=head[i];p;p=G[p].next)
    if (!(deg[V1]<=B && V1<i))
      for (int q=G[p].next;q;q=G[q].next)
        if (!(deg[V2]<=B && V2<i))
          if (HashMap::query(G[p].v,G[q].v))
        Ans++;
    }else
      s[++pnt]=i;
  for (int i=1;i<=pnt;i++)
    for (int j=i+1;j<=pnt;j++)
      if (HashMap::query(s[i],s[j]))
    for (int k=j+1;k<=pnt;k++)
      if (HashMap::query(s[j],s[k]) && HashMap::query(s[i],s[k]))
        Ans++;
  printf("%d\n",Ans);
  return 0;
}

Solution2

可以证明似乎三角形的个数就是 O(mm)
求无向图三元环的个数,这种做法是O(m*sqrt(m))的吗?

给定n个点m条边的稀疏图,对于每一条边,枚举两个端点中度数较少的端点的邻接点,判断是否构成三元环。这种做法的判断次数在m条边形成一个完全图时达到最大,即O(m*sqrt(m))。不知道这个结论是不是对的?

#include<cstdio>
#include<cstdlib>
#include<algorithm>
#include<vector>
#include<cmath>
#define push_back pb
using namespace std;

inline char nc(){
  static char buf[100000],*p1=buf,*p2=buf;
  return p1==p2&&(p2=(p1=buf)+fread(buf,1,100000,stdin),p1==p2)?EOF:*p1++;
}
inline void read(int &x){
  char c=nc(),b=1;
  for (;!(c>='0' && c<='9');c=nc()) if (c=='-') b=-1;
  for (x=0;c>='0' && c<='9';x=x*10+c-'0',c=nc()); x*=b;
}

const int N=200005;

namespace HashMap{
  const int P=1000007,seed=2333;
  int u[N<<1],v[N<<1],next[N<<1];
  int head[P],inum;
  inline void add(int _u,int _v){
    int t=(_u*seed+_v)%P;
    u[++inum]=_u; v[inum]=_v; next[inum]=head[t]; head[t]=inum;
  }
  inline bool query(int _u,int _v){
    int t=(_u*seed+_v)%P;
    for (int p=head[t];p;p=next[p])
      if (u[p]==_u && v[p]==_v)
    return 1;
    return 0;
  }
}

struct edge{
  int u,v,next;
}G[N<<1];
int head[N],inum;
inline void add(int u,int v,int p){
  G[p].u=u; G[p].v=v; G[p].next=head[u]; head[u]=p;
}

int n,m;
int u[N],v[N];
int deg[N];
int Ans;
#define V G[p].v
int main(){
  freopen("t.in","r",stdin);
  freopen("t2.out","w",stdout);
  read(n); read(m);
  for (int i=1;i<=m;i++){
    read(u[i]),read(v[i]);
    add(u[i],v[i],++inum); add(v[i],u[i],++inum);
    deg[u[i]]++,deg[v[i]]++;
    HashMap::add(u[i],v[i]),HashMap::add(v[i],u[i]);
  }
  for (int i=1;i<=m;i++){
    int u=::u[i],v=::v[i];
    if (deg[u]>deg[v]) swap(u,v);
    for (int p=head[u];p;p=G[p].next)
      if (v!=V && HashMap::query(v,V))
    Ans++;
  }
  printf("%d\n",Ans/3);
  return 0;
}

学习了这里的写法 其实是一样的 不过常数小很多

#include<cstdio>
#include<cstdlib>
#include<algorithm>
#include<vector>
#include<cmath>
#define push_back pb
using namespace std;

inline char nc(){
  static char buf[100000],*p1=buf,*p2=buf;
  return p1==p2&&(p2=(p1=buf)+fread(buf,1,100000,stdin),p1==p2)?EOF:*p1++;
}
inline void read(int &x){
  char c=nc(),b=1;
  for (;!(c>='0' && c<='9');c=nc()) if (c=='-') b=-1;
  for (x=0;c>='0' && c<='9';x=x*10+c-'0',c=nc()); x*=b;
}

const int N=200005;

namespace HashMap{
  const int P=1000007,seed=2333;
  int u[N<<1],v[N<<1],next[N<<1];
  int head[P],inum;
  inline void add(int _u,int _v){
    int t=(_u*seed+_v)%P;
    u[++inum]=_u; v[inum]=_v; next[inum]=head[t]; head[t]=inum;
  }
  inline bool query(int _u,int _v){
    int t=(_u*seed+_v)%P;
    for (int p=head[t];p;p=next[p])
      if (u[p]==_u && v[p]==_v)
    return 1;
    return 0;
  }
}

struct edge{
  int u,v,next;
}G[N<<1];
int head[N],inum;
inline void add(int u,int v,int p){
  G[p].u=u; G[p].v=v; G[p].next=head[u]; head[u]=p;
}

int n,m;
int deg[N];
int Ans;
#define V G[p].v
int s[N],pnt;
int main(){
  int iu,iv;
  freopen("t.in","r",stdin);
  freopen("t3.out","w",stdout);
  read(n); read(m);
  for (int i=1;i<=m;i++){
    read(iu),read(iv);
    add(iu,iv,++inum); add(iv,iu,++inum);
    deg[iu]++,deg[iv]++;
    HashMap::add(iu,iv),HashMap::add(iv,iu);
  }
  for (int u=1;u<=n;u++){
    pnt=0;
    for (int p=head[u];p;p=G[p].next)
      if (deg[u]<deg[V] || (deg[u]==deg[V] && u<V))
    s[++pnt]=V;
    for (int i=1;i<=pnt;i++)
      for (int j=1;j<i;j++)
    if (HashMap::query(s[i],s[j]))
      Ans++;
  }
  printf("%d\n",Ans);
  return 0;
}

UPD.找到题了 跑得并不快 555 好像大家各有奇技淫巧

BZOJ 3498: PA2009 Cakes

#include<cstdio>
#include<cstdlib>
#include<algorithm>
#include<vector>
#include<cmath>
#define push_back pb
using namespace std;
typedef long long ll;

inline char nc(){
  static char buf[100000],*p1=buf,*p2=buf;
  return p1==p2&&(p2=(p1=buf)+fread(buf,1,100000,stdin),p1==p2)?EOF:*p1++;
}
inline void read(int &x){
  char c=nc(),b=1;
  for (;!(c>='0' && c<='9');c=nc()) if (c=='-') b=-1;
  for (x=0;c>='0' && c<='9';x=x*10+c-'0',c=nc()); x*=b;
}

const int N=250005;

namespace HashMap{
  const int P=1000007,seed=2333;
  int u[N<<1],v[N<<1],next[N<<1];
  int head[P],inum;
  inline void add(int _u,int _v){
    int t=(_u*seed+_v)%P;
    u[++inum]=_u; v[inum]=_v; next[inum]=head[t]; head[t]=inum;
  }
  inline bool query(int _u,int _v){
    int t=(_u*seed+_v)%P;
    for (int p=head[t];p;p=next[p])
      if (u[p]==_u && v[p]==_v)
    return 1;
    return 0;
  }
}

struct edge{
  int u,v,next;
}G[N<<1];
int head[N],inum;
inline void add(int u,int v,int p){
  G[p].u=u; G[p].v=v; G[p].next=head[u]; head[u]=p;
}

int n,m;
int deg[N];
ll Ans;
int val[N];
#define V G[p].v
int s[N],pnt;
int main(){
  int iu,iv;
  freopen("t.in","r",stdin);
  freopen("t.out","w",stdout);
  read(n); read(m);
  for (int i=1;i<=n;i++) read(val[i]);
  for (int i=1;i<=m;i++){
    read(iu),read(iv);
    add(iu,iv,++inum); add(iv,iu,++inum);
    deg[iu]++,deg[iv]++;
    HashMap::add(iu,iv),HashMap::add(iv,iu);
  }
  for (int u=1;u<=n;u++){
    pnt=0;
    for (int p=head[u];p;p=G[p].next)
      if (deg[u]<deg[V] || (deg[u]==deg[V] && u<V))
    s[++pnt]=V;
    for (int i=1;i<=pnt;i++)
      for (int j=1;j<i;j++)
    if (HashMap::query(s[i],s[j]))
      Ans+=max(val[u],max(val[s[i]],val[s[j]]));
  }
  printf("%lld\n",Ans);
  return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值