8.7考试总结(NOIP模拟)[Smooth·Six·Walker]

前言

踩了挺多以前没踩过的坑。。。

T1 一开始是打了一个 60pts 的 DFS ,在与暴力拍了几组数据保证正确性之后,

突然想到 BFS 可能会更快一些,然后就又码了一个 BFS,又和 DFS 拍了200组数据,

发现 BFS 确实快,然后就交了一个 BFS 然后我就直接 \(60pts\rightarrow 0pts\)

后来看了一下特殊性质该了一下边界就 80pts了,肝疼QAQ

然后就是 T3 以前一直是在 结构体里读入的,这回就翻车了。

还有就是 map 的重载小于号最好不要和等于号一样,不然就挂分了。。

T1 Smooth

解题思路

官方题解说和蚯蚓有一点像(尽管我没有做过这个题

其实就是开了 b 个队列,每次选择所有队首元素中最小的就是光滑数。

然后对于编号大于等于刚才所选队列的加入一个新元素就好了。

因为每次都是从小到大,因此可以达到不重复的目的。

code

80pts

#include<bits/stdc++.h>
#define int long long
#define ull unsigned long long
#define f() cout<<"Pass"<<endl
using namespace std;
inline int read()
{
	int x=0,f=1;
	char ch=getchar();
	while(ch>'9'||ch<'0')
	{
		if(ch=='-')	f=-1;
		ch=getchar();
	}
	while(ch>='0'&&ch<='9')
	{
		x=(x<<1)+(x<<3)+(ch^48);
		ch=getchar();
	}
	return x*f;
}
const int N=7e7+10,M=1e7;
int INF;
int k,b,ans,pb[20]={0,2,3,5,7,11,13,17,19,23,29,31,37,41,43,47};
set<int> s;
void dfs(int x,int cnt)
{
	if(cnt>INF||cnt<0)	return ;
	if(x==b+1)
	{
		s.insert(cnt);
		if(s.size()>k)	s.erase((--s.end()));
		return ;
	}
	int pre=cnt;
	for(int i=1;cnt<=INF&&cnt>0;i++)
	{
		dfs(x+1,cnt);
		cnt=cnt*pb[x];
	}
}
signed main()
{
	b=read();
	k=read();
	if(b==2)	INF=1e18;
	else	INF=7e7;
	dfs(1,1);
	printf("%lld",(*s.rbegin()));
	return 0;
}

正解

#include<bits/stdc++.h>
#define int long long
#define ull unsigned long long
#define f() cout<<"Pass"<<endl
using namespace std;
inline int read()
{
	int x=0,f=1;
	char ch=getchar();
	while(ch>'9'||ch<'0')
	{
		if(ch=='-')	f=-1;
		ch=getchar();
	}
	while(ch>='0'&&ch<='9')
	{
		x=(x<<1)+(x<<3)+(ch^48);
		ch=getchar();
	}
	return x*f;
}
const int INF=1e18;
queue<int> q[20];
int k,ans,b,pb[20]={0,2,3,5,7,11,13,17,19,23,29,31,37,41,43,47};
signed main()
{
	b=read();
	k=read();
	for(int i=1;i<=b;i++)
		q[i].push(pb[i]);
	while(k!=1)
	{
		int minn=INF,id;
		for(int i=1;i<=b;i++)
			if(minn>q[i].front())
			{
				minn=q[i].front();
				id=i;
			}
		q[id].pop();
		ans=minn;
		for(int i=id;i<=b;i++)
			q[i].push(ans*pb[i]);
		k--;
	}
	printf("%lld",ans);
	return 0;
}

T2 Six

解题思路

挺好的一个题,主要有两种做法:状压,状压+记忆化DFS

我当然是选择裸的状压了(雾

首先不难发现我们只关心 n 的质因数的种类和数量,因此不需要记录对应的数值。

发现对于所含质因数种类相同的两个数在某种意义上是等价的。

因此我们可以将此压缩成为一类数,记录这一类数的数量就好了。

然后就可以愉快的状压了。

采用 8 进制进行记录,0 表示没有出现过这个质因数

1~6 表示与此数同时计入中编号最小的数的编号

7则表示这个数字出现过两次。

然后就是状压,并对于已经有的状态的基础上进行 DP 就好了。

code

48pts DFS

#include<bits/stdc++.h>
#define int long long
#define ull unsigned long long
#define f() cout<<"Pass"<<endl
using namespace std;
inline int read()
{
	int x=0,f=1;
	char ch=getchar();
	while(ch>'9'||ch<'0')
	{
		if(ch=='-')	f=-1;
		ch=getchar();
	}
	while(ch>='0'&&ch<='9')
	{
		x=(x<<1)+(x<<3)+(ch^48);
		ch=getchar();
	}
	return x*f;
}
const int N=4e7+10,mod=1e9+7;
int n,ans,num[N],t,tot,p[10],q[10],s[N];
int cnt,pri[N];
bool vis[N];
void Prime()
{
	vis[1]=true;
	for(int i=2;i<=min((int)sqrt(n)+1,40000000ll);i++)
	{
		if(!vis[i])	pri[++cnt]=i;
		for(int j=1;j<=cnt&&i*pri[j]<=min((int)sqrt(n)+1,40000000ll);j++)
		{
			vis[i*pri[j]]=true;
			if(i%pri[j]==0)	break;
		}
	}
}
void dfs(int x,int all)
{
	if(x==tot+1)
	{
		if(all!=1)	num[++t]=all;
		return ;
	}
	dfs(x+1,all);
	for(int i=1;i<=q[x];i++)
	{
		all*=p[x];
		dfs(x+1,all);
	}
}
int gcd(int x,int y)
{
	if(!y)	return x;
	return gcd(y,x%y);
}
void dfs2(int pos)
{
	ans=(ans+1)%mod;
	for(int i=1;i<=t;i++)
	{
		int sum=0;
		for(int j=1;j<pos&&sum<=1;j++)
			if(gcd(s[j],num[i])!=1)
				sum++;
		if(sum<=1)
		{
			s[pos]=num[i];
			dfs2(pos+1);
		}
	}
}
signed main()
{
	n=read();
	Prime();
	for(int i=1;i<=cnt&&n!=1;i++)
	{
		if(n%pri[i])	continue;
		p[++tot]=pri[i];
		while(n%pri[i]==0)
		{
			q[tot]++;
			n/=pri[i];
		}
	}
	if(n!=1)	p[++tot]=n,q[tot]=1;
	dfs(1,1);
	dfs2(1);
	printf("%lld",ans-1);
	return 0;
}

正解

#include<bits/stdc++.h>
#define int long long
#define ull unsigned long long
#define f() cout<<"Pass"<<endl
using namespace std;
inline int read()
{
	int x=0,f=1;
	char ch=getchar();
	while(ch>'9'||ch<'0')
	{
		if(ch=='-')	f=-1;
		ch=getchar();
	}
	while(ch>='0'&&ch<='9')
	{
		x=(x<<1)+(x<<3)+(ch^48);
		ch=getchar();
	}
	return x*f;
}
const int N=(1<<18)+10,M=(1<<6)+10,mod=1e9+7;
int n,cnt,ans,f[N],s[N],fac[10],sub[M];
signed main()
{
	n=read();
	for(int i=2;i<=sqrt(n)+1;i++)
		if(n%i==0)
		{
			cnt++;
			while(n%i==0)
			{
				fac[cnt]++;
				n/=i;
			}
		}
	if(n!=1)	fac[++cnt]=1;
	fill(sub+1,sub+(1<<cnt)+1,1);
	for(int i=1;i<(1<<cnt);i++)
		for(int j=1;j<=cnt;j++)
			if((i>>j-1)&1)
				sub[i]*=fac[j];
	f[0]=1;
	for(int i=0;i<(1<<3*cnt);i++)
		if(f[i])
			for(int j=1;j<(1<<cnt);j++)
			{
				int pos=0,sta=i,jud=0;
				bool check=false;
				for(int k=1;k<=cnt;k++)
					if(!((i>>(k-1)*3)&7)&&((j>>k-1)&1))
					{
						pos=k;
						break;
					}
				for(int k=1;k<=cnt;k++)
					if(((i>>(k-1)*3)&7)&&((j>>k-1)&1))
					{
						if(((i>>(k-1)*3)&7)==7||(jud&&jud!=((i>>(k-1)*3)&7)))
						{
							check=true;
							break;
						}
						if(!jud)	jud=((i>>(k-1)*3)&7);
					}
				if(check)	continue;
				for(int k=1;k<=cnt;k++)
					if((j>>k-1)&1)
					{
						if((i>>(k-1)*3)&7)	sta|=7<<(k-1)*3;
						else	sta|=pos<<(k-1)*3;
					}
				f[sta]=(f[sta]+f[i]*sub[j]%mod)%mod;
			}
	for(int i=1;i<(1<<3*cnt);i++)
		ans=(ans+f[i])%mod;
	printf("%lld",ans);
	return 0;
}

T3 Walker

解题思路

随机化算法是正解还是第一次见。

随便挑出两组数,然后高斯消元求出来三个操作的数。

然后进行回代看是否有一半以上的数满足条件。

有一个小细节:对于 \([-\dfrac{\pi}{2},\dfrac{\pi}{2}]\)类似的区间里

不同的弧度的 cos 值是相同的,这个时候就需要用 sin 来判断一下了。

code

#include<bits/stdc++.h>
#define int long long
using namespace std;
const int N=1e5+10;int n;
double eps=1e-6,s[10][10];
struct node
{
	double x,y,x2,y2,x3,y3;
}q[N];
int random(int l,int r)
{
	int x=rand()*rand()%(r-l+1)+l;
	while(x<=0)	x=rand()*rand()%(r-l+1)+l;
	return x;
}
void gaosi(int n,int m)
{
	for(int i=1;i<=n;i++)
	{
		int pos=0;
		for(int j=1;j<m;j++)	if(s[i][j]){pos=j;break;}
		if(s[i][pos]!=1&&s[i][pos])
		{
			double temp=s[i][pos];
			for(int j=pos;j<=m;j++)	s[i][j]/=temp;
		}
		for(int j=i+1;j<=n;j++)
		{
			if(!s[j][pos])	continue;
			double temp=s[j][pos];
			for(int k=pos;k<=m;k++)	s[j][k]-=s[i][k]*temp;
		}
	}
	for(int i=n;i>=2;i--)
	{
		int pos=0;
		for(int j=1;j<m;j++)	if(s[i][j]){pos=j;break;}
		if(s[i][pos]!=1&&s[i][pos])
		{
			double temp=s[i][pos];
			for(int j=pos;j<=m;j++)	s[i][j]/=temp;
		}
		for(int j=1;j<i;j++)
		{
			if(!s[j][pos])	continue;
			double temp=s[j][pos];
			for(int k=pos;k<=m;k++)	s[j][k]-=s[i][k]*temp;
		}
	}
}
signed main()
{
	srand((unsigned)time(0));	scanf("%lld",&n);
	for(int i=1;i<=n;i++)	scanf("%lf%lf%lf%lf",&q[i].x,&q[i].y,&q[i].x2,&q[i].y2);
	while(1)
	{
		int i=random(1,n),j=random(1,n),sum=0;
		if(i==j)	continue;
		s[1][1]=q[i].x;s[1][2]=-q[i].y;s[1][3]=1;s[1][4]=0;s[1][5]=q[i].x2;
		s[2][1]=q[i].y;s[2][2]=q[i].x;s[2][3]=0;s[2][4]=1;s[2][5]=q[i].y2;
		s[3][1]=q[j].x;s[3][2]=-q[j].y;s[3][3]=1;s[3][4]=0;s[3][5]=q[j].x2;
		s[4][1]=q[j].y;s[4][2]=q[j].x;s[4][3]=0;s[4][4]=1;s[4][5]=q[j].y2;
		gaosi(4,5);
		double cs,ss,scal,dx,dy,cos,sin,tmp=1;
		for(int k=1;k<=4;k++)	if(fabs(s[k][1])>eps)	cs=s[k][5];
		for(int k=1;k<=4;k++)	if(fabs(s[k][2])>eps)	ss=s[k][5];
		for(int k=1;k<=4;k++)	if(fabs(s[k][3])>eps)	dx=s[k][5];
		for(int k=1;k<=4;k++)	if(fabs(s[k][4])>eps)	dy=s[k][5];
		scal=sqrt(cs*cs+ss*ss);cos=cs/scal;sin=ss/scal;
		if(sin<0)	tmp=-1;
		for(int k=1;k<=n;k++)	if(fabs((q[k].x*cos-q[k].y*sin)*scal+dx-q[k].x2)<=eps&&fabs((q[k].y*cos+q[k].x*sin)*scal+dy-q[k].y2)<=eps)	sum++;
		if(sum>=(n>>1)){printf("%.11lf\n%.11lf\n%.11lf %.11lf",acos(cos)*tmp,scal,dx,dy);return 0;}
	}
	return 0;
}
上一篇:【算法题】获取单向链表中倒数第 N 个节点


下一篇:iOS数据持久化文件读写之偏好设置