2021.8.21NOIP模拟赛总结

Hello,大家好!

我是蒟亦先生!

温馨提示:请各位奆奆们洁身自好,请勿COPY!


题解:

unimodal


代码实现+解析

我们根据数据其实不难得出,

n的全排列中单峰排列的个数为2的n-1次方,

所以可以用快速幂解决


更严谨的说法:

f(1)=1;

f(2)=2;

对于f(n),n可以放在n-1的任意组合

且只能放在n-1的前面或后面

故f(n)=f(n-1)*2;

f(n)=2的n-1次方

#include<bits/stdc++.h>
using namespace std;
long long n,i,j;
long long pqow(long long a,long long b)//快速幂 
{
	long long sx=a,ans=1;
	while(b!=0)
	{
		if(b&1!=0)
		{
			ans*=sx;
			ans%=1234567;
		}
		sx*=sx;
		sx%=1234567;
		b/=2;
	}
	return ans%1234567;
}
int main()
{
	cin>>n;
	n--;
	cout<<pqow(2,n);
	return 0;
}

grid


代码实现+解析

本题的思路很简单:并查集+最小生成树 优先考虑纵向,再不行连接横向 一共需要三次并查集操作 (详情请看代码)

#include<bits/stdc++.h>
using namespace std;
const int maxn=1e6+1;
long long n,m,f[maxn],x,y,x2,y2,ans,i,j;
long long find(long long z)
{
	return f[z]==z?z:f[z]=find(f[z]);
}
int main()
{
	cin>>n>>m;
	for(i=1;i<=n;i++)
	{
		for(j=1;j<=m;j++)
		{
			f[m*(i-1)+j]=m*(i-1)+j;
		}
	}
	while(cin>>x>>y>>x2>>y2)
	{
		int k1=find(m*(x-1)+y),k2=find(m*(x2-1)+y2);//第一次
		if(k1!=k2)
		{
			f[k1]=k2;
		}
	}
	for(i=1;i<=m;i++)//优先纵向,边权为1
	{
		for(j=1;j<n;j++)
		{
			int k1=find(m*(j-1)+i),k2=find(m*j+i);//第二次
			if(k1!=k2)
			{
				ans++;
				f[k1]=k2;
			}
		}
	}
	for(i=1;i<=n;i++)//纵向连不完,再连接横向,边权为2
	{
		for(j=1;j<m;j++)
		{
			int k1=find(m*(i-1)+j),k2=find(m*(i-1)+j+1);//第3次
			if(k1!=k2)
			{
				ans+=2;
				f[k1]=k2;
			}
		}
	}
	cout<<ans;
	return 0;
}

pair


代码实现+解析

把关系看做边,问题转化求一条边不重复的路径,即欧拉路径。

先判断有没有连通性和两个奇点

接着循环寻找与x相连的边(x,u)——删边

然后将x插入栈中

这样可以保证找到的一定是字典序最小的合法方案

#include<bits/stdc++.h>
using namespace std;
const int maxn=500;
int n,st,cnt,bz,d[maxn],f[maxn],a[maxn][maxn];
char ans[maxn];
int find(int z)
{
	return f[z]==z?z:f[z]=find(f[z]);
}
void dfs(int x)
{
	for(int i=1;i<=maxn;i++)
	{
		if(a[x][i])
		{
			a[x][i]=a[i][x]=0;
			dfs(i);
		}
	}
	ans[n--]=x;
}
int main()
{
	int i,j;
	scanf("%lld",&n);
	for(i=1;i<=maxn;i++)
	{
		f[i]=i;
	}
	for(i=1;i<=n;i++)
	{
		char ch[3];
		scanf("%s",ch);
		int x=ch[0],y=ch[1];
		a[x][y]=a[y][x]=1;
		d[x]++;
		d[y]++;
		f[find(x)]=find(y);
	}
	for(i=1;i<=maxn;i++)//判断连通性
	{
		if(f[i]==i&&d[i])
		{
			bz++;
		}
	}
	if(bz!=1)
	{
		printf("No Solution");
		return 0;
	}
	for(i=1;i<=maxn;i++)//判奇点(为链)
	{
		if(d[i]&1)
		{
			cnt++;
			if(!st)
			{
				st=i;
			}
		}
	}
	if(cnt&&cnt!=2)
	{
		printf("No Solution");
		return 0;
	}
	if(!st)//判环,找到最小点
	{
		for(i=0;i<maxn;i++)
		{
			if(d[i])
			{
				st=i;
				break;
			}
		}
	}
	dfs(st);
	printf("%s",ans);
	return 0;
}

transfer


代码实现+解析

本题看上去是一道数论题,实际上是一个图论题。

题目要求——

每个点可以转换成约数和的条件是和大小小于当前的数。

所以每个点要么可以向前变,要么没有向前变。

如果以约数为根,那么就得到许多的树,

也就是说,他们是一些森林,约数和是父亲。

然后求最长的变换距离(最长连),做一个DP即可。

#include<bits/stdc++.h>
using namespace std;
const int maxn=55555;
long long n,ans,i,j,sum[maxn],dis1[maxn],dis2[maxn];
int main()
{
	cin>>n;
	for(i=1;i<=n;i++)
	{
		for(j=2;i*j<=n;j++)
		{
			sum[i*j]+=i;
		}
	}
	for(i=n;i>=1;i--)
	{
		j=sum[i];
		if(j<i)
		{
			if(dis1[i]+1>dis1[j])
			{
				dis2[j]=dis1[j];
				dis1[j]=dis1[i]+1;
			}
			else if(dis1[i]+1>dis2[j])
			{
				dis2[j]=dis1[i]+1;
			}
		}
	}
	for(i=1;i<=n;i++)
	{
		ans=max(ans,dis1[i]+dis2[i]);
	}
	cout<<ans;
	return 0;
}

谢谢各位奆奆!

题解制作不易,望能用心思考

如有疑问,欢迎提出问题一起讨论!

本章完

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值