咱們發現對於一個詢問K,答案必定是<k的,而且一個數只會出現它因子的個數的次數,for example:6=1*6,6*1,2*3,3*2;四次;c++
因此咱們考慮二分答案,每次對於一個mid求 SUM=\(\sum_{i=1}^{mid}\frac{mid}{i}\),而後與k比較一下就好了,經典的問題。app
這道題的作法很暴力,咱們能夠將邊權從小到大排序,而後一條一條加進去,若是有衝突,就把這個環中最小的邊刪除就好了。
Why?由於邊是從小到大的,加進來的邊只會愈來愈大,而咱們要求差值最小的,因此刪除最小的邊最優,同時這個操做也不影響圖自己的聯通性。因此也算是一個巧妙的想法吧。spa
#include<cstdio> #include<cstring> #include<algorithm> #include<bitset> using namespace std; bitset<2001>e[2001]; int i,j,n,m,k,l,t,num,cnt,fa[2001],ff[2001][2001],aim,pd,bbz[20001],ans; struct nup {int l,r,id,qz;}f[80001],heap[80001]; bool cmp(nup x,nup y){return x.qz<y.qz;} int father(int x) {if(x!=fa[x]) return fa[x]=father(fa[x]);return x;} void down(long long x) { while(x*2<=num) { long long y=x*2; if(y+1<=num&&heap[y+1].qz<heap[y].qz) y++; if(heap[y].qz>=heap[x].qz) break; swap(heap[y].qz,heap[x].qz);swap(heap[y].id,heap[x].id); x=y; } } void dfs(int x,int father,int mi,int fx,int fy) { if(x==aim) { pd=0;e[fx].reset(fy);e[fy].reset(fx); bbz[ff[fx][fy]]=1;ff[fx][fy]=ff[fy][fx]=0; return; } if(pd==0) return; for(int i=e[x]._Find_first();i!=e[x].size();i=e[x]._Find_next(i)) if(i!=father) { if(f[ff[x][i]].qz<mi) dfs(i,x,f[ff[x][i]].qz,x,i); else dfs(i,x,mi,fx,fy); } } int main() { freopen("graph.in","r",stdin); freopen("graph.out","w",stdout); scanf("%d",&t); while(t>0) { t--; scanf("%d%d",&n,&m); for(i=1;i<=m;++i) scanf("%d%d%d",&f[i].l,&f[i].r,&f[i].qz); sort(f+1,f+1+m,cmp); memset(heap,0,sizeof heap);cnt=0;num=0;ans=987654321; memset(ff,0,sizeof ff); memset(bbz,0,sizeof bbz); for(i=1;i<=n;i++)fa[i]=i,e[i].reset(); for(i=1;i<=m;i++) { int va1=father(f[i].l),va2=father(f[i].r); if(va1!=va2) { ff[f[i].l][f[i].r]=ff[f[i].r][f[i].l]=i; cnt++;fa[va1]=va2;e[f[i].l].set(f[i].r);e[f[i].r].set(f[i].l); num++;heap[num].qz=f[i].qz;heap[num].id=i; } else { aim=f[i].r,pd=1;dfs(f[i].l,0,987654321,0,0); num++;heap[num].qz=f[i].qz;heap[num].id=i; e[f[i].l].set(f[i].r);e[f[i].r].set(f[i].l); ff[f[i].l][f[i].r]=ff[f[i].r][f[i].l]=i; } if(cnt==(n-1)) { while(bbz[heap[1].id]==1) { swap(heap[1].id,heap[num].id);swap(heap[1].qz,heap[num].qz);num--;down(1); } ans=min(f[i].qz-heap[1].qz,ans); } } if(ans==987654321) printf("-1\n");else printf("%d\n",ans); } }
這也是一個很巧妙的題目,若是咱們設DP:f[i][j][k]表示如今完成了前i個,分了j組,和爲k,咱們能夠看到這很難轉移,又要記錄最大最小值什麼的,因此咱們不妨設f[i][j][k]表示完成前i個還有j組未完成分配極差和爲k(前提先將A1,A2,\(\cdots\),An從小到大排序),這時咱們就不用記錄最大最小值了,why?對於一個分組a1,a2,a3\(\cdots\cdots\)an(a1<a2<\(\cdots\cdots\)<an) 極差和=an-a1=\(\sum_{i=2}^{n}a[i]-a[i-1]\) ,因此咱們往未完成分配的組裏加上a[i]-a[i-1]的貢獻就好了。
設 tmp=(a[i]-a[i-1])*j 接下來分類討論:
1.新開一組第一個數是a[i],而且未完成:f[i][j+1][k+tmp]+=f[i-1][j][k];(由於有j個組是未完成的因此加上tmp沒問題,下一輪是a[i+1]-a[i],若是不加tmp則每一組就會少a[i]-a[i-1]; 新開的不用加)
2.新開一組就只有一個數是a[i] :f[i][j][k+tmp]+=f[i-1][j][k];
if(j>0)
{
3.a[i]加到舊的一組中,而且未完成:f[i][j][k+tmp]+=f[i-1][j][k]*j;(有j個組能夠選)
4.a[i]加到舊的一組中,而且完成了:f[i][j-1][k]+=f[i-1][j][k]*j;
}
就上面四種狀況記得取模 1000000007;code
#include<cstdio> #include<cstring> #include<algorithm> using namespace std; typedef long long ll; const int mo=1000000007; ll i,j,n,m,l,k,f[2][201][4001],a[10001],now,tmp,ans; int main() { freopen("group.in","r",stdin); freopen("group.out","w",stdout); scanf("%d%d",&n,&k); for(i=1;i<=n;i++) scanf("%d",&a[i]); sort(a+1,a+n+1); f[0][0][0]=1;now=0; for(i=1;i<=n;i++) { now^=1; memset(f[now],0,sizeof f[now]); for(j=0;j<i;j++) { tmp=(a[i]-a[i-1])*j; for(l=0;l<=k;l++) { if(tmp+l>k) break; f[now][j+1][l+tmp]+=f[1-now][j][l],f[now][j+1][l+tmp]%=mo; f[now][j][l+tmp]+=f[1-now][j][l];f[now][j][l+tmp]%=mo; if(j>0) { f[now][j][l+tmp]+=f[1-now][j][l]*j;f[now][j][l+tmp]%=mo; f[now][j-1][l+tmp]+=f[1-now][j][l]*j;f[now][j-1][l+tmp]%=mo; } } } } for(i=0;i<=k;i++) ans+=f[now][0][i],ans%=mo; printf("%lld",ans); return 0; }
完結撒花🌸🌸🌸🌸🌸🌸:happy:💠排序