P4208 [JSOI2008]最小生成樹計數
阿新 • • 發佈:2020-08-21
思路
剛看到的時候,因為 \((n\leq 100)\) ,所以想到了爆搜,但是這樣做顯然會 \(TLE\) ,所以我們手摸幾組資料找找結論
然後能發現一個結論:一張圖上的不同最小生成樹中,權值相等的邊的個數是不變的
小證明:用kruskal求最小生成樹時,每一步都是最優的,如果有不同的最小生成樹,則當前步的權值必然小於等於之前最小生成樹當前步的選擇。但是反證可得,如果有小於的話,此時的最小生成樹就比之前的優了,和之前矛盾,所以權值相等的邊的個數是不變的。
程式碼
#include<bits/stdc++.h> using namespace std; const int mod=31011; struct node{ int from,to,w; }e[1010]; struct kruskal{ int l,r,v; }a[1010];//存i邊的個數,l r是左右端點,v是i在最小生成樹上的個數 int n,m,f[110],ans,q[110],num,sum; int find(int x){return x==f[x]?x:find(f[x]);} //不能路徑壓縮!!! bool cmp(node a,node b){ return a.w<b.w; } void dfs(int x,int now,int k)//x是你當前找的值 now是第幾個邊 k是你選了的個數 { if(now==a[x].r+1){ if(k==a[x].v) sum++;//保證和生成樹所需的一樣 return ; } int xx=find(e[now].from),yy=find(e[now].to); if(xx!=yy)//看是否選這邊就為環 { f[xx]=yy; dfs(x,now+1,k+1);//選 f[xx]=xx;f[yy]=yy;//復原 } dfs(x,now+1,k);//不選 } int main(){ scanf("%d%d",&n,&m); for(int i=1;i<=n;i++) f[i]=i; for(int i=1;i<=m;i++) scanf("%d%d%d",&e[i].from,&e[i].to,&e[i].w); sort(e+1,e+m+1,cmp); int tot=0; for(int i=1;i<=m;i++){ if(e[i].w!=e[i-1].w) num++,a[num].l=i,a[num-1].r=i-1; int xx=find(e[i].from),yy=find(e[i].to); if(xx!=yy) f[xx]=yy,a[num].v++,tot++; }//kruskal if(tot!=n-1){ printf("0"); return 0; }//如果構不成樹,就可以輸出0了 a[num].r=m; ans=1; for(int i=1;i<=n;i++) f[i]=i; for(int i=1;i<=num;i++){ sum=0; dfs(i,a[i].l,0); ans=(ans*sum)%mod; for(int j=a[i].l;j<=a[i].r;j++){ int xx=find(e[j].from),yy=find(e[j].to); if(xx!=yy) f[xx]=yy; }//弄完一個後,連起來保證不為環 } printf("%d",ans); return 0; }