• bzoj 1016: [JSOI2008]最小生成树计数【dfs+克鲁斯卡尔】


    有一个性质就是组成最小生成树总边权值的若干边权总是相等的
    这意味着按边权排序后在权值相同的一段区间内的边能被选入最小生成树的条数是固定的
    所以先随便求一个最小生成树,把每段的入选边数记录下来
    然后对于每一段dfs找合法方案即可,注意dfs中需要退回并查集,所以用不路径压缩的并查集
    然后根据乘法定理,把每一段dfs后的结果乘起来即可。

    #include<iostream>
    #include<cstdio>
    #include<algorithm>
    using namespace std;
    const int N=1005,mod=31011;
    int n,m,ans=1,sum,tot,cnt,l[N],r[N],c[N],f[N];
    struct qwe
    {
    	int u,v,w;
    }a[N];
    bool cmp(const qwe &a,const qwe &b)
    {
    	return a.w<b.w;
    }
    int zhao(int x)
    {
    	return x==f[x]?x:zhao(f[x]);
    }
    void dfs(int q,int w,int k)
    {
    	if(w==r[q]+1)
    	{
    		if(k==c[q])
    			sum++;
    		return;
    	}
    	int fu=zhao(a[w].u),fv=zhao(a[w].v);
    	if(fu!=fv)
    	{
    		f[fu]=fv;
    		dfs(q,w+1,k+1);
    		f[fu]=fu,f[fv]=fv;
    	}
    	dfs(q,w+1,k);
    }
    int main()
    {
    	scanf("%d%d",&n,&m);
    	for(int i=1;i<=m;i++)
    		scanf("%d%d%d",&a[i].u,&a[i].v,&a[i].w);
    	sort(a+1,a+1+m,cmp);
    	for(int i=1;i<=n;i++)
    		f[i]=i;
    	for(int i=1;i<=m;i++)
    	{
    		if(a[i].w!=a[i-1].w)
    			r[cnt]=i-1,l[++cnt]=i;
    		int fu=zhao(a[i].u),fv=zhao(a[i].v);
    		if(fu!=fv)
    			tot++,c[cnt]++,f[fu]=fv;
    	}
    	if(tot!=n-1)
    	{
    		puts("0");
    		return 0;
    	}
    	r[cnt]=m;
    	for(int i=1;i<=n;i++)
    		f[i]=i;
    	for(int i=1;i<=cnt;i++)
    	{
    		sum=0;
    		dfs(i,l[i],0);
    		ans=ans*sum%mod;
    		for(int j=l[i];j<=r[i];j++)
    		{
    			int fu=zhao(a[j].u),fv=zhao(a[j].v);
    			if(fu!=fv)
    				f[fu]=fv;
    		}
    	}
    	printf("%d
    ",ans);
    	return 0;
    }
    
  • 相关阅读:
    数据库生成说明
    Android 的 SurfaceView 双缓冲应用
    一些and知识 和ui
    weibo11
    android总结
    weibo14
    weibo9
    weibo12
    weibo10
    在线人数的统计
  • 原文地址:https://www.cnblogs.com/lokiii/p/9248995.html
Copyright © 2020-2023  润新知