三元环&哈希表

标题这俩东西看起来似乎八杆子打不着,不过只是我在学三元环的时候才刚学到哈希表是个什么东西。。。
关于三元环的解法感觉Claris写的就很不错→%%Claris
我在这里再简单的说一下复杂度的相关;
首先可以改成单向边,全都改成由大的点指向小的点(我自己写的是相反的。。。)然后枚举每一个点i,再枚举这个点的出边,如果连出的这个点(设为k)出度小于sqrt(m)的话,再暴力枚举一下另一个点,只看这一部分复杂度是n根号m的;
如果连出的点k的出度大于sqrt(m),那么就暴力找所有i的其他出点,看是否能组成三元环,由于出度大于sqrt(m)的最多只有sqrt(m)个点,所以这一部分是m根号m的;
至于第二种情况找第三个点的时候为什么要从一开始的i开始找,如果i出度大于根m,那i和k可能的组合是很少的,而如果i出度小于根号m,那i向外找的点是小于根号m的,总体平均下来还是m根号m
不得不说一下Claris神奇的写法,他给每条边先排了个序,再按顺序插入邻接表中,这样保证每个点向外依次遍历到的点是单调的,可以省掉一部分常数;
至于哈希表,之前一直以为哈希表只是简单的哈希,把值模一下放数组里就完了,以至于我无数次脸黑撞哈希;
今天我才刚发现,原来真正的哈希表是不会有哈希冲突的,其原理是将哈希值相同的数据存一个邻接链表,每次都在这个邻接链表里遍历一遍看是否有要查找的元素;
这样一来正确性是可以保证的,而且由于撞哈希的概率不会太高,复杂度均摊下来也是O(1)的;

放一波代码orz(感觉表达能力好弱。。。写的这些个鬼东西将来自己都不一定能再看懂)

bzoj3498: PA2009 Cakes

#include<cstdio>
#include<cstring>
#include<cmath>
#include<algorithm>
#define LL long long
const int maxn=100005;
using std::max;
const int maxm=250005;
int sq,v[maxn],du[maxn],n,m;
struct asd{
    int next,x,y;
}edge[maxm*4];
const int mod=(1<<23)-1;
int hnode[mod+1],node[maxn];
int etot=0;
void add(int x,int y){
    edge[++etot].next=node[x];
    edge[etot].y=y;
    node[x]=etot;
}
int hash(int x,int y){
    return (x<<8|y)&mod;
}
void insert(int x,int y){
    int val=hash(x,y);
    edge[++etot].next=hnode[val];
    edge[etot].x=x,edge[etot].y=y;
    hnode[val]=etot;
}
bool vis(int x,int y){
    int val=hash(x,y);
    for(int i=hnode[val];i;i=edge[i].next)if(edge[i].x==x&&edge[i].y==y)return true;
    return false;
}
void read(int &x){
    x=0;
    char c=getchar();
    while(c<'0'||c>'9')c=getchar();
    while(c>='0'&&c<='9'){
        x=x*10+c-'0';
        c=getchar();
    }
}
int main()
{
//  freopen("in.txt","r",stdin);
    scanf("%d%d",&n,&m);
    sq=sqrt((double)m)+0.5;
    for(int i=1;i<=n;i++)read(v[i]);
    while(m--){
        int tp1,tp2;
        read(tp1),read(tp2);
        if(tp1>tp2)std::swap(tp1,tp2);
        add(tp1,tp2);
        du[tp1]++;
        insert(tp1,tp2);
    }   
    LL ans=0;
    for(int i=1;i<=n;i++){
        for(int j=node[i];j;j=edge[j].next){
            int vi=v[edge[j].y]> v[i] ? v[edge[j].y] : v[i];
            if(du[edge[j].y]<sq){
                for(int k=node[edge[j].y];k;k=edge[k].next)
                    if(vis(i,edge[k].y))ans+=(LL)v[edge[k].y]>vi ? v[edge[k].y] : vi;
            }
            else {
                for(int k=node[i];k;k=edge[k].next){
                    if(edge[k].y>edge[j].y&&vis(edge[j].y,edge[k].y))ans+=(LL)v[edge[k].y]>vi ? v[edge[k].y] : vi;
                }
            }
        }
    }
    printf("%lld\n",ans);
    return 0;
}

bzoj5206: [Jsoi2017]原力

#include<cstdio>
#include<cstring>
#include<cmath>
#include<algorithm>
#define LL long long
const int maxn=100005;
using std::max;
const int maxm=250005;
int sq,du[maxn],n,m;
struct asd{
    int next,x,y,c;LL vi;
}edge[maxm*4];
const int mod=999983;
const int p=1000000007;
int hnode[mod],node[maxn];
int etot=0;
int add(int x,int y,LL v,int zt){
    edge[++etot].next=node[x];
    edge[etot].y=y;
    edge[etot].c=zt;
    edge[etot].vi=v;
    node[x]=etot;
    return etot;
}
int hash(int x,int y,int z){
    return(int)(((LL)x*(LL)n*8ll+(LL)y*8ll+z)%(LL)mod);
}
void insert(int x,int y,LL v,int zt){
    int val=hash(x,y,zt);
    edge[++etot].next=hnode[val];
    edge[etot].x=x,edge[etot].y=y;
    edge[etot].c=zt;
    edge[etot].vi=v;
    hnode[val]=etot;
}
int tot3=0;
int vis(int x,int y,int zt){
    int val=hash(x,y,zt);
    for(int i=hnode[val];i;i=edge[i].next){
        tot3++;
        if(edge[i].x==x&&edge[i].y==y&&edge[i].c==zt)return i;
    }
    return 0;
}
void read(int &x){
    x=0;
    char c=getchar();
    while(c<'0'||c>'9')c=getchar();
    while(c>='0'&&c<='9'){
        x=x*10+c-'0';
        c=getchar();
    }
}
int main()
{
//  freopen("force.in","r",stdin);
//  freopen("force.out","w",stdout); 
//  freopen("in.txt","r",stdin);
    scanf("%d%d",&n,&m);
    sq=sqrt((double)m)+0.5;
    while(m--){
        int tp1,tp2,tp3;
        read(tp1),read(tp2),read(tp3);
        char c[3];
        scanf("%s",c);
        int zt=0;
        if(c[0]=='R')zt=1;
        else if(c[0]=='G')zt=1<<1;
        else zt=1<<2;
        if(tp1>tp2)std::swap(tp1,tp2);
        int tv=vis(tp1,tp2,zt);
        if(!tv){
            add(tp1,tp2,(LL)tp3,zt);
            insert(tp1,tp2,(LL)tp3,zt);
            du[tp1]++;

        }
        else {
            (edge[tv].vi+=(LL)tp3)%=p,(edge[tv-1].vi+=(LL)tp3)%=p;

        }
    }   
    int ans=0;
    for(int i=1;i<=n;i++){
        for(int j=node[i];j;j=edge[j].next){
            int zt=edge[j].c;
            if(du[edge[j].y]<=sq){
                for(int k=node[edge[j].y];k;k=edge[k].next)
                if(zt^edge[k].c)
                {
                    int nzt=7^zt^edge[k].c;
                    int tv=vis(i,edge[k].y,nzt);
                    if(tv)(ans+=(LL)(edge[tv].vi*edge[k].vi%p)*edge[j].vi%p)%=p;
                }
            }
            else {
                for(int k=node[i];k;k=edge[k].next)
                if(zt^edge[k].c)
                {
                    int nzt=7^zt^edge[k].c;
                    int tv=vis(edge[j].y,edge[k].y,nzt);
                    if(edge[k].y>edge[j].y&&tv)(ans+=(LL)(edge[tv].vi*edge[k].vi%p)*edge[j].vi%p)%=p;
                }
            }
        }
    }
    printf("%d\n",ans);
    return 0;
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值