• bzoj 5206 [Jsoi2017]原力


    LINK:原力

    一张无向图 这道题统计三元环的价值和。有重边但是无自环。

    我曾经写过三元环计数 这个和那个题差不太多。

    不过有很多额外操作 对于重边问题 我们把所有颜色相同的重边缩在一起 这样的话我们就可以针对点来运算了。

    不过这样做开邻接表就很困难了 (直接map爽...

    接下来是正规的三元环计数了 我们考虑重新建图 度数小的连向度数大的 度数相等的由编号小的连向编号大的。

    可以暴力枚举每个点 标记所到达的点(暴力枚举合并过后的边 再枚举一条边去统计答案 这样就完事了。

    可以观察一下复杂度 外层暴力枚举是O(m)的 内层我们考虑再枚举一条边 度数小于sqrt(m)的点复杂度为sqrt(m)

    大于sqrt(m)的因为连向的只有>sqrt(m)的所以复杂度还是sqrt(m).

    那其实内层均摊msqrt(m) 外层枚举好像跟内层复杂度没有联系 所以总复杂度 msqrt(m).

    这里简述另外一种分治法:(这种分治法更容易分析复杂度。

    对于三个点度数大于sqrt(m)的 我们可以直接暴力枚举三个点 复杂度显然msqrt(m).

    反之 枚举一个点 其两条出边来判断 不重不漏的话我们规定编号最小的点能够枚举到这个环即可。

    可以看出后者复杂度 msqrt(m).

    对于这两种分治法 一般叫做根号分治 非常行之有效的方法。

    运用第一种方法 我T的不省人事。

    const int MAXN=100010;
    int n,m,len;
    struct wy{int x,y;}t[MAXN];
    int ru[MAXN];
    map<pii,int>H[3];
    char a[10];
    int lin[MAXN],ver[MAXN],nex[MAXN];
    inline void add(int x,int y){ver[++len]=y;nex[len]=lin[x];lin[x]=len;}
    int main()
    {
        //freopen("1.in","r",stdin);
        gt(n);gt(m);
        rep(1,m,i)
        {
            int x,y,z;
            gt(x);gt(y);gt(z);gc(a);
            int w=a[1]=='R'?0:a[1]=='G'?1:2;
            if(x>y)swap(x,y);++ru[x];++ru[y];
            H[w][mk(x,y)]=(H[w][mk(x,y)]+z)%mod;
            t[i]=(wy){x,y};
        }
        for(int i=1;i<=m;++i)
        {
            int x=t[i].x,y=t[i].y;
            if(ru[x]==ru[y]||ru[x]<ru[y])add(x,y);
            else add(y,x);
        }
        ll ans=0;
        for(int i=1;i<=n;++i)
        {
            for(int j=lin[i];j;j=nex[j])
            {
                int tn=ver[j];
                for(int k=lin[tn];k;k=nex[k])
                {
                    int w1=ver[k];
                    ans=(ans+(ll)H[1][mk(min(i,tn),max(i,tn))]*H[2][mk(min(tn,w1),max(tn,w1))]*H[0][mk(min(i,w1),max(i,w1))])%mod;
                    ans=(ans+(ll)H[1][mk(min(i,tn),max(i,tn))]*H[0][mk(min(tn,w1),max(tn,w1))]*H[2][mk(min(i,w1),max(i,w1))])%mod;
                    ans=(ans+(ll)H[2][mk(min(i,tn),max(i,tn))]*H[1][mk(min(tn,w1),max(tn,w1))]*H[0][mk(min(i,w1),max(i,w1))])%mod;
                    ans=(ans+(ll)H[2][mk(min(i,tn),max(i,tn))]*H[0][mk(min(tn,w1),max(tn,w1))]*H[1][mk(min(i,w1),max(i,w1))])%mod;
                    ans=(ans+(ll)H[0][mk(min(i,tn),max(i,tn))]*H[1][mk(min(tn,w1),max(tn,w1))]*H[2][mk(min(i,w1),max(i,w1))])%mod;
                    ans=(ans+(ll)H[0][mk(min(i,tn),max(i,tn))]*H[2][mk(min(tn,w1),max(tn,w1))]*H[1][mk(min(i,w1),max(i,w1))])%mod;
                }
            }
        }
        putl(ans);
        //cout<<H[1][mk(min(1,2),max(1,2))]<<endl;
        return 0;
    }
    
    const int MAXN=100010,M=10000007;
    int n,m,len,len1;
    struct wy{int x,y,z,e;}t[MAXN];
    int ru[MAXN];char a[10];
    struct jl
    {
    	int x,y,w;
    	jl(){x=y=w=0;};
    	jl(int a,int b,int c){x=a;y=b;w=c;}
    	inline int friend operator ==(jl a,jl b){return a.x==b.x&&a.y==b.y&&a.w==b.w;}
    }s[M],s1;
    int lin[MAXN],ver[MAXN],nex[MAXN],e[MAXN],e1[MAXN];
    inline void add(int x,int y,int z,int z1){ver[++len]=y;nex[len]=lin[x];lin[x]=len;e[len]=z;e1[len]=z1;}
    int lin1[M],nex1[M],v[M];
    inline int ha1(int x,int y,int w){return ((x*P+w)%M*P+y+w*y%M*x)*P%M;}
    inline int find(int x,int y,int w,int z)
    {
    	s1=jl(x,y,w);
    	int xx=ha1(x,y,w);
    	for(int i=lin1[xx];i;i=nex1[i])
    		if(s1==s[i]){v[i]=(v[i]+z)%mod;return v[i];}
    	return 0;
    }
    inline void ha(int x,int y,int w,int z)
    {
    	if(find(x,y,w,z))return;
    	int ww=((x*P+w)%M*P+y+w*y%M*x)*P%M;
    	s[++len1]=s1;
    	nex1[len1]=lin1[ww];
    	lin1[ww]=len1;
    	v[len1]=z;
    }
    int main()
    {
    	freopen("1.in","r",stdin);
    	gt(n);gt(m);
    	rep(1,m,i)
    	{
    		int x,y,z;
    		gt(x);gt(y);gt(z);gc(a);
    		int w=a[1]=='R'?0:a[1]=='G'?1:2;
    		if(x>y)swap(x,y);++ru[x];++ru[y];
    		t[i]=(wy){x,y,w,z};ha(x,y,w,z);
    	}
    	for(int i=1;i<=m;++i)
    	{
    		int x=t[i].x,y=t[i].y,w=t[i].z,z=t[i].e;
    		if(ru[x]==ru[y]||ru[x]<ru[y])add(x,y,w,z);
    		else add(y,x,w,z);
    	}
    	ll ans=0;
    	for(int i=1;i<=n;++i)
    	{
    		for(int j=lin[i];j;j=nex[j])
    		{
    			for(int k=lin[ver[j]];k;k=nex[k])
    			{
    				int w1=ver[k];
    				if(e[k]==e[j])continue;
    				int w2=i;if(w1>w2)swap(w1,w2);
    				int ww=find(w1,w2,3-e[j]-e[k],0);
    				ans=(ans+(ll)e1[j]*e1[k]%mod*ww)%mod;
    			}
    		}
    	}
    	putl(ans);
    	return 0;
    }
    
    
    const int MAXN=50010;
    struct wy
    {
    	int x,y,z;
    	wy(){x=y=z=0;}
    	wy(int a,int b,int c){x=a;y=b;z=c;}
    	bool operator<(const wy a)const{return x==a.x?y==a.y?z<a.z:y<a.y:x<a.x;}
    };
    map<wy,int>H;
    int lin[MAXN],ver[MAXN<<2],nex[MAXN<<2],e[MAXN<<2],e1[MAXN<<2];
    int n,m,si,len,cnt,d[MAXN],id[MAXN],tot;
    char a[5];
    inline void add(int x,int y,int z,int z1)
    {
    	ver[++len]=y;nex[len]=lin[x];lin[x]=len;e[len]=z;e1[len]=z1;
    }
    int main()
    {
    	freopen("1.in","r",stdin);
    	gt(n);gt(m);si=(int)sqrt(m*1.0);
    	ll ans=0;
    	rep(1,m,i)
    	{
    		int x,y,z;
    		gt(x);gt(y);gt(z);gc(a);
    		int w=a[1]=='R'?1:a[1]=='G'?2:3;
    		add(x,y,z,w);add(y,x,z,w);
    		++d[x];++d[y];
    		(H[wy(x,y,w)]+=z)%=mod;(H[wy(y,x,w)]+=z)%=mod;
    	}
    	rep(1,n,i)if(d[i]>=si)id[++tot]=i;
    	rep(1,tot,i)rep(1,tot,j)rep(1,tot,k)
    		ans=(ans+(ll)H[wy(id[i],id[j],1)]*H[wy(id[j],id[k],2)]%mod*H[wy(id[i],id[k],3)])%mod;
    	rep(1,n,i)
    	{
    		if(d[i]<si)
    		for(int j=lin[i];j;j=nex[j])
    		{
    			if(d[ver[j]]>=si||ver[j]>i)
    			for(int k=nex[j];k;k=nex[k])
    				if(e1[j]!=e1[k]&&(d[ver[k]]>=si||ver[k]>i))
    					ans=(ans+(ll)H[wy(ver[j],ver[k],6-e1[j]-e1[k])]*e[j]%mod*e[k])%mod;
    		}
    	}
    	putl(ans);return 0;
    }
    
    

    上面 是两个版本的 第一种方法外面分析复杂度是正确的 可就是跑的过于慢了 加上hash也是如此。

    不得不采用第一种方法 我们把大于sqrt的抽出来单独跑 考虑不重不漏 显然一个三元环我们以唯一方向寻找 那么就不会重复。

    考虑小的点 显然我么小的点枚举边 外层O(m) 内层<sqrt(m) 注意不重不漏 那么我们定义一个环由编号最小的节点开始即可。

    代码也放上面好了.

    郁闷的是 为什么第一种方法一直T 我也不知道为啥。

  • 相关阅读:
    Python tkinter 实现简单登陆注册 基于B/S三层体系结构,实现用户身份验证
    Python3连接MySQL数据库实战
    Python3 报错'latin-1' codec can't encode character 解决方案
    python 操作excle 之第三方库 openpyxl学习
    对象的深拷贝和浅拷贝
    手机wap站全屏展示隐藏地址栏和状态栏代码
    JS调用App方法及App调用JS方法
    Vue里给v-html元素添加样式
    为什么JavaScript中移动端使用ontouchend无法获取touches数组
    什么是并发和并行
  • 原文地址:https://www.cnblogs.com/chdy/p/12555061.html
Copyright © 2020-2023  润新知