zoukankan      html  css  js  c++  java
  • 【bzoj5206】[Jsoi2017]原力 根号分治+STL-map

    题目描述

    一个原力网络可以看成是一个可能存在重边但没有自环的无向图。每条边有一种属性和一个权值。属性可能是R、G、B三种当中的一种,代表这条边上原力的类型。权值是一个正整数,代表这条边上的原力强度。原力技术的核心在于将R、G、B三种不同的原力融合在一起产生单一的、便于利用的原力。为了评估一个能源网络,JYY需要找到所有满足要求的三元环(首尾相接的三条边),其中R、G、B三种边各一条。一个三元环产生的能量是其中三条边的权值之积。
    现在对于给出的原力网络,JYY想知道这个网络的总能量是多少。网络的总能量是所有满足要求三元环的能量之和。

    输入

    第一行包含两个正整数N、M。表示原力网络的总顶点个数和总边数。
    接下来M行,每行包含三个正整数ui,vi,wi和一个字符ci。
    表示编号ui和vi的顶点之间存在属性为ci权值为wi的一条边。
    N≤50,000,M≤100,000,1≤?Wi≤10^6

    输出

    输出一行一个整数,表示这个原力网络的总能量模10^9+7的值

    样例输入

    4 6
    1 2 2 R
    2 4 3 G
    4 3 5 R
    3 1 7 G
    1 4 11 B
    2 3 13 B

    样例输出

    828


    题解

    根号分治+STL-map

    看到这种根本没法写出什么玄学数据结构之类的,大概率就是根号分治了。

    对于本题,由于边数只有 $m$ ,因此度数大于等于 $sqrt m$ 的点只有 $O(sqrt m)$ 个,我们称这样的点为大点,度数小于 $sqrt m$ 的称为小点。

    那么对于一个三元环:

    如果三个点都是大点:这种情况下我们暴力枚举三个大点,求出是否有满足条件的三元环并加入到答案中即可。时间复杂度为 $O((sqrt m)^3)=O(msqrt m)$ ;

    如果三个点中有小点:这种情况下我们枚举每个小点和它的两条出边,判断这三个点是否有满足条件的三元环。此时,枚举第一条出边相当于枚举图中所有边,第二条出边是度数复杂度,而度数小于 $sqrt m$ ,因此复杂度也是 $O(msqrt m)$ 的。注意这个过程需要保证不重不漏,因此只考虑枚举点为这三个点中编号最小的小点的答案。

    那么如何判断是否有满足条件的三元环呢?我偷懒了使用STL-map判断两点之间有没有某颜色的边,复杂度上会多一个log。

    时间复杂度 $O(msqrt mlog m)$ ,实际上跑得挺快的 然而在bz上还是倒数第一... 

    #include <map>
    #include <cmath>
    #include <cstdio>
    #define N 50010
    #define mod 1000000007
    using namespace std;
    typedef long long ll;
    struct data
    {
    	int x , y , z;
    	data() {}
    	data(int a , int b , int c) {x = a , y = b , z = c;}
    	bool operator<(const data &a)const {return x == a.x ? y == a.y ? z < a.z : y < a.y : x < a.x;}
    };
    map<data , ll> mp;
    int head[N] , to[N << 2] , val[N << 2] , opt[N << 2] , next[N << 2] , cnt , d[N] , id[350] , tot;
    char str[5];
    inline void add(int x , int y , int v , int c)
    {
    	to[++cnt] = y , val[cnt] = v , opt[cnt] = c , next[cnt] = head[x] , head[x] = cnt;
    }
    int main()
    {
    	int n , m , si , i , j , k , x , y , z , t;
    	ll ans = 0;
    	scanf("%d%d" , &n , &m) , si = (int)sqrt(m);
    	for(i = 1 ; i <= m ; i ++ )
    	{
    		scanf("%d%d%d%s" , &x , &y , &z , str);
    		t = (str[0] == 'R' ? 1 : str[0] == 'G' ? 2 : 3);
    		add(x , y , z , t) , add(y , x , z , t) , d[x] ++ , d[y] ++ ;
    		(mp[data(x , y , t)] += z) %= mod , (mp[data(y , x , t)] += z) %= mod;
    	}
    	for(i = 1 ; i <= n ; i ++ )
    		if(d[i] >= si)
    			id[++tot] = i;
    	for(i = 1 ; i <= tot ; i ++ )
    		for(j = 1 ; j <= tot ; j ++ )
    			for(k = 1 ; k <= tot ; k ++ )
    				ans = (ans + mp[data(id[i] , id[j] , 1)] * mp[data(id[i] , id[k] , 2)] % mod * mp[data(id[j] , id[k] , 3)]) % mod;
    	for(i = 1 ; i <= n ; i ++ )
    		if(d[i] < si)
    			for(j = head[i] ; j ; j = next[j])
    				if(d[to[j]] >= si || to[j] > i)
    					for(k = next[j] ; k ; k = next[k])
    						if(opt[k] != opt[j] && (d[to[k]] >= si || to[k] > i))
    							ans = (ans + mp[data(to[j] , to[k] , 6 - opt[j] - opt[k])] * val[j] % mod * val[k]) % mod;
    	printf("%lld
    " , ans);
    	return 0;
    }
    
  • 相关阅读:
    android中kl布局文件加载失败解决办法
    android系统输入按键流程
    linux键值转android键值配置文件
    linux键值到Android键值的转换与自定义
    linux中ioctl的应用与说明
    zabbix邮件告警
    linux 双网关双IP设置
    随笔
    记录一次事故
    python解析.yml/.yaml文件--pyyaml模块(第三方)
  • 原文地址:https://www.cnblogs.com/GXZlegend/p/8564496.html
Copyright © 2011-2022 走看看