2021.01.27【NOIP提高A组】模拟
5434. 【NOIP2017提高A组集训10.30】Matrix
我们发现对于一个询问K,答案一定是<k的,并且一个数只会出现它因子的个数的次数,for example:6=1*6,6*1,2*3,3*2;四次;
所以我们考虑二分答案,每次对于一个mid求 SUM=\(\sum_{i=1}^{mid}\frac{mid}{i}\),然后与k比较一下就行了,经典的问题。
5435. 【NOIP2017提高A组集训10.30】Graph
这道题的做法很暴力,我们可以将边权从小到大排序,然后一条一条加进去,如果有冲突,就把这个环中最小的边删除就行了。
Why?因为边是从小到大的,加进来的边只会越来越大,而我们要求差值最小的,所以删除最小的边最优,同时这个操作也不影响图本身的联通性。所以也算是一个巧妙的想法吧。
#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);
}
}
5436. 【NOIP2017提高A组集训10.30】Group
这也是一个很巧妙的题目,如果我们设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;
#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:💠