首页 > 其他分享 >题解P2143 [JSOI2010] 巨额奖金

题解P2143 [JSOI2010] 巨额奖金

时间:2022-08-18 20:35:50浏览次数:49  
标签:JSOI2010 ch P2143 题解 ll register long isdigit

题意就是让你求有多少种最小生成树

生成树用 kruskal 求就好了

我们考虑用 dfs 中用乘法原理去计数

#include<bits/stdc++.h>
#define N 1000100
using namespace std;
typedef long long ll;
ll n,m,cnt[N],fa[N],ans=1,c[N];
const ll mod=31011;
struct edge{
    ll u,v,w;
}e[N];
inline ll fr(){
    register ll s=0,k=1;register char ch=getchar();
    while(!isdigit(ch)){if(ch=='-') k=-1;ch=getchar();};
    while(isdigit(ch)){s=(s<<3)+(s<<1)+ch-'0';ch=getchar();};
    return s*k;
}
inline bool cmp(const edge& x,const edge& y){
    return x.w<y.w;
}
inline ll find(ll x){
    return fa[x]==x?x:find(fa[x]);//由于我们合并后还要拆开,所以绝对不能路径压缩 
}
inline bool kruskal(){//kruskal最小生成树 
    ll tot=0;
    for(ll i=1;i<=n;i++) fa[i]=i;
    for(ll i=1;i<=m;i++){
        ll x=find(e[i].u),y=find(e[i].v);//查找 
        if(x!=y){
            tot++;
            cnt[e[i].w]++;
            fa[x]=y;
        }
        if(tot==n-1) return true;
    }
    return false;
}
inline ll dfs(ll now,ll w,ll s){
    if(s==cnt[w]) return 1ll;//如果等于了则算为一种方案 
    if(e[now].w!=w) return 0ll;
    ll sum=0,x=find(e[now].u),y=find(e[now].v);
    if(x!=y){
        fa[x]=y;//合并 
        sum=dfs(now+1,w,s+1);//把这个答案记进来 
        fa[x]=x,fa[y]=y;//在拆开,方便下面找边时继续合并 
    }
    return sum+dfs(now+1,w,s); //算出总的方案数 
}
int main(){
    scanf("%lld%lld",&n,&m);
    for(ll i=1;i<=m;i++){
        scanf("%lld%lld%lld",&e[i].u,&e[i].v,&e[i].w),c[i]=e[i].w;
    }
    sort(c+1,c+m+1);
    sort(e+1,e+m+1,cmp);
    ll len=unique(c+1,c+m+1)-c-1;
    for(ll i=1;i<=m;i++){
        e[i].w=lower_bound(c+1,c+len+1,e[i].w)-c;//离散化 
    }
    if(!kruskal()){
        puts("0");
        return 0;
    }
    for(ll i=1;i<=n;i++) fa[i]=i;
    for(ll i=1;i<=m;i++){
        if(e[i].w==e[i-1].w||!cnt[e[i].w]) continue;//相同的边我们一起处理,如果没有这个权值的边,同样continue 
        ans=ans*dfs(i,e[i].w,0ll)%mod;//计数,运用乘法原理 
        ll j=i;
        while(e[j].w==e[i].w){//只要相等,则合并 
            ll x=find(e[j].u),y=find(e[j].v);
            fa[x]=y;
            j++;//每次找新的权值相等的边 
        }
    }
    printf("%lld",ans%mod);
    return 0;
}

 

标签:JSOI2010,ch,P2143,题解,ll,register,long,isdigit
From: https://www.cnblogs.com/dreamau/p/16600011.html

相关文章

  • ARC097E题解
    感觉挺一眼的啊?众所周知如果序列\(i\)要通过相邻两项交换变成\(p_i\),那么交换次数就是\(\sum_{i<j}[p_i>p_j]\),或者说线段\((i,p_i)\)相交的对数。于是一个很naiv......
  • 【题解】 洛谷P3694 邦邦的大合唱站队
    发现尽管\(n\)比较大,但\(m\)非常小,于是考虑状压。记\(dp_{i}\)表示满足条件的乐队集合为\(i\)时的最小出队人数,\(dp_i=\min\{dp_{i\\xor\\1<<k}\}+w_{i\\xo......
  • [CF1450F] The Struggling Contestant 题解
    \(\mathtt{Link}\)CF1450FTheStrugglingContestant-洛谷|计算机科学教育新生态(luogu.com.cn)\(\mathtt{Description}\)\(T\)组数据。一共有\(n\)道题,题号......
  • P5080 Tweetuzki 爱序列 题解
    题目传送门更好地阅读体验题目大意Tweetuzki有一个长度为\(n\)的序列\(a_1,a_2,\cdots,a_n\)。他希望找出一个最大的\(k\),满足在原序列中存在一些数\(b_1,b......
  • IOI 2022 简要题解
    考前写题解增加RP。D1T1:考虑按照列DP。对于每一列选择的鱼的区间进行决策。每列中被选择的y坐标最大的鱼,需要被左面或右面覆盖。假设我们决策好了前i列的方案,考虑第i列......
  • [题解] HDU 5115 Dire Wolf 区间DP
    考虑先枚举所有的物品中最后拿走的,这样就分成了2个子问题,即先拿完左边的,再拿完右边的,最后拿选出的那个。令dp(i,j)表示拿完[i,j]所有物品的最小代价。你可能会说,我们拿[i,j......
  • 题解 CF1575D【Divisible by Twenty-Five】
    值域非常小,其中只有\(4\times10^6\)个数是\(25\)的倍数,因此可以暴力枚举所有位数正确的\(25\)的倍数,然后检查是否合法。检查方法就是枚举每一位,如果是数字就必须一......
  • CF Round 814 Div2 题解
    A题ChipGame(签到)给定一个\(n\)行\(m\)列的方格矩阵,左下角是\((1,1)\),右上角是\((m,n)\)。(采取了类似笛卡尔坐标系的表示法,不是普通的\(x\)行\(y\)列)Burenka......
  • CF578C题解
    看到题面的第一眼是这玩意儿关于x是单谷的,证明稍微想了一下:设\(f[k]\)和\(g[k]\)是原序列中长度为\(k\)的子区间的最大子区间和最小子区间,给定\(x\)时答案就相......
  • CF1719C Fighting Tournament 题解
    思路根据题意,很容易看出,每个人都完成一次比赛后,即完成\(n-1\)轮之后,力量值最大的人会留在第一的位置,且在第\(n-1\)轮完成后,除了力量值最大的人,其他人的胜场数都不会再......