zoukankan      html  css  js  c++  java
  • [BZOJ 1016] [JSOI2008] 最小生成树计数 【DFS】

    题目链接:BZOJ - 1016

    题目分析

    最小生成树的两个性质:

    同一个图的最小生成树,满足:

    1)同一种权值的边的个数相等

    2)用Kruscal按照从小到大,处理完某一种权值的所有边后,图的连通性相等

    这样,先做一次Kruscal求出每种权值的边的条数,再按照权值从小到大,对每种边进行 DFS, 求出这种权值的边有几种选法。

    最后根据乘法原理将各种边的选法数乘起来就可以了。

    特别注意:在DFS中为了在向下DFS之后消除决策影响,恢复f[]数组之前的状态,在DFS中调用的Find()函数不能路径压缩。 

    代码

    #include <iostream>
    #include <cstdlib>
    #include <cstring>
    #include <cmath>
    #include <cstdio>
    #include <algorithm>
    
    using namespace std;
    
    const int MaxN = 100 + 5, MaxM = 1000 + 5, Mod = 31011;
    
    int n, m, Top, Sum, Cnt;
    int f[MaxN], L[MaxM], R[MaxM], Num[MaxM];
    
    typedef long long LL;
    LL Ans;
    
    struct Edge
    {
    	int u, v, w; 
    } E[MaxM];
    
    inline bool Cmp(Edge e1, Edge e2) 
    {
    	return e1.w < e2.w;
    }
    
    inline int Find(int x, int o) 
    {
    	int i, j, k;
    	j = x; 
    	while (j != f[j]) j = f[j];
    	if (o == 1) return j;
    	i = x;
    	while (i != j) 
    	{
    		k = i;
    		i = f[i];
    		f[k] = j;
    	}
    	return j;
    }
    
    void DFS(int Type, int x, int y) 
    {
    	if (x == R[Type] + 1) 
    	{
    		if (y == Num[Type]) ++Cnt;
    		return;
    	}
    	int fx, fy;
    	fx = Find(E[x].u, 1); fy = Find(E[x].v, 1);
    	if (fx != fy)
    	{
    		f[fx] = fy;
    		DFS(Type, x + 1, y + 1);
    		f[fx] = fx;
    	}
    	DFS(Type, x + 1, y);
    }
    
    int main() 
    {
    	scanf("%d%d", &n, &m);
    	for (int i = 1; i <= m; ++i)
    		scanf("%d%d%d", &E[i].u, &E[i].v, &E[i].w);
    	sort(E + 1, E + m + 1, Cmp);
    	Top = 0; Sum = 0;
    	int fx, fy;
    	for (int i = 1; i <= n; ++i) f[i] = i;
    	for (int i = 1; i <= m; ++i) 
    	{
    		if (i == 1 || E[i].w != E[i - 1].w) L[++Top] = i;
    		R[Top] = i;
    		fx = Find(E[i].u, 0); fy = Find(E[i].v, 0);
    		if (fx != fy) 
    		{
    			f[fx] = fy;
    			++Num[Top];
    			++Sum;
    		}
    	}
    	if (Sum != n - 1) 
    	{
    		printf("0
    ");
    		return 0;
    	}
    	for (int i = 1; i <= n; ++i) f[i] = i;
    	Ans = 1;
    	for (int i = 1; i <= Top; ++i) 
    	{
    		if (Num[i] == 0) continue;
    		Cnt = 0;
    		DFS(i, L[i], 0);
    		Ans = Ans * (LL)Cnt % Mod;
    		for (int j = L[i]; j <= R[i]; ++j) 
    		{
    			fx = Find(E[j].u, 0); fy = Find(E[j].v, 0);
    			if (fx != fy) f[fx] = fy;
    		}
    	}
    	printf("%d
    ", (int)Ans);
    	return 0;
    }
    

      

  • 相关阅读:
    text-overflow white-space word-break word-wrap word-spacing line-clamp 傻傻分不清楚0.0=>文本超出显示省略号/数字英文字母折行有关css 属性/显示两行,第二行省略号显示css方法
    jq 操作表单中 checkbox 全选 单选
    用 pdf.js兼容部分安卓显示PDF在线预览 时,a标签直接链接参数文件不能含中文的解决办法
    通过form实现enter事件
    小白随笔之数组的方法
    引用类型之Array
    Reset
    js常用事件
    让女朋友能懂的网络技术篇之动态代理
    图论之Dijkstra算法
  • 原文地址:https://www.cnblogs.com/JoeFan/p/4319280.html
Copyright © 2011-2022 走看看