【題解】[牛客OI周賽4-提高組]A.K小生成樹 列舉+字首和+剪枝
阿新 • • 發佈:2018-12-18
#include<cstdio> #include<algorithm> using namespace std; const int N=1e6+10,Q=1e4+10,INF=0x3f3f3f3f; int n,m,a[N],fa[21],l,r,f1,f2,ans,tot,flag,q,askl[Q],askr[Q],minl=INF,maxr=-INF; struct Edge{ int u,v,w; }e[26]; inline int Find(int x){return x==fa[x]?x:fa[x]=Find(fa[x]);} int main() { //freopen("in.txt","r",stdin); 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); scanf("%d",&q); for(int i=1;i<=q;i++)//離線讀入 { scanf("%d%d",&askl[i],&askr[i]); minl=min(askl[i],minl);maxr=max(askr[i],maxr); } for(int i=1;i<(1<<m);i++)//列舉邊集 { ans=i;tot=0;flag=1; while(ans) { ans&=(ans-1);tot++; } if(tot!=n-1)flag=0;//如果不為n-1條邊,剪枝 if(flag) { ans=0; for(int j=1;j<=n;j++) fa[j]=j; for(int j=1;j<=m;j++) if(i&(1<<(j-1))) { ans+=e[j].w;f1=Find(e[j].u);f2=Find(e[j].v); if(f1==f2){flag=0;break;}//如果成環,剪枝 else fa[f1]=f2; } if(flag&&ans>=minl&&ans<=maxr)a[ans-minl+1]++;//只記錄[minl,maxr],優化字首和陣列大小 } } for(int i=1;i<=maxr-minl+1;i++)a[i]+=a[i-1];//變成字首和 for(int i=1;i<=q;i++) printf("%d\n",a[askr[i]-minl+1]-a[askl[i]-minl]); return 0; }
總結
因為邊數小,可以直接列舉邊集,離線讀入和字首和處理以及區間偏移是關鍵。