咱们发现对于一个询问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:💠排序