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:💠

posted @ 2021-02-01 20:05  *LZX*  阅读(87)  评论(0编辑  收藏  举报