NOIP 2023 模拟赛 20230712 C 论剑

发布时间 2023-07-12 20:29:06作者: Juye_Scene

首先是伟大的题面
image
然后是数据范围
image

先解决1-4号数据点
1.枚举每个gcd的值p,统计一次答案,得到最小值(期望得分20)

\[ans=\min_{p=2}^{\max a}\sum^n_{i=1}\min(a_i \bmod p,p-(a_i \bmod p)(a>p )) \]

2.我们可以发现p仅在为质数时不会重复,也可以将p换为质数(期望得分40)

两种的时间复杂度:
1.$$O(n\max a)$$
2.$$O(nP(\max a))$$

博主比较蒟蒻,只会带log的求质数,所以复杂度为\(O(n\log(\max a))\),也可以拿到40
(实际上我只枚举到1000,但还是用40分的代码拿到80分)

继续发呆思考
可以发现gcd为2时,每个\(a_i\)只需要+1或-1(\(a_i\neq 1\)
答案不会超过n

惊人的发现:
修改的次数超过1的最多有\(\frac n 2\)个(可以用反证法证明,若修改超过1的超过\(\frac n 2\)个则答案会大于n)
我们又可以得到修改次数不超过1的(即修改次数为1的)大于\(\frac n 2\)
有什么用呢?没有什么用

如果我们找到了修改次数不超过1的,那它修改后可能为\(a_i-1(a\neq1)、a_i、a_i+1\),而答案的质因数就在里面
我们只需要预处理出质数,求出\(a_i-1(a\neq1)、a_i、a_i+1\)的所有质因数,每个像1、2中那样跑一遍,即可求出答案
复杂度\(O(n)\),如此优秀,直接碾过去

什么,万一这个不是修改不超过1的数?没关系,抽到的概率至少大于50%,稳赚不亏
保守的办法是枚举\(\frac n 2 +1\)个数,但复杂度又变回了\(O(n^2P(a_i))\)运气不好甚至没有40分,所以我说没有用

既然提到了“保守”,那肯定有“激进”的方法
看眼复杂度,如果\(n^2\)可以变为\(n\log n\),那就可以有惊无险的通过这道题
那我们就抽\(\log n\)个数,每个跑一次上述的方法

这就是正解了,但不完全正解(
可以想到,如果抽到的数中,没有一个是我们想要的,那就可以喜提“WA”了
算下概率
每次选择都有至少一半的概率,总共抽\(\log n\)
概率为\(1-\frac 1 {n^{\log n}}\),在后面几个点中\(\log n\)大概为20左右遇到可以买彩票了
这个概率是很低的,根据“能AC的就是正解”,可以知道这就是一个正经的正解

WARN:前几个点n较小时会出现\(\log n\)较小的情况,可能会喜提彩票一等奖
所以我们要给选的树设置个下界,或者将选的个数直接定为20左右

喜闻乐见的代码时间:

哦,对了,为了防止万恶的脑洞比较大的出题人将前面几个数全部设为不满足的数
我们可以加入随机数,或者直接看RP(

博主的超长代码
#include<bits/stdc++.h>
#define fo(s) freopen(s".in","r",stdin),freopen(s".out","w",stdout)
#define Ts template<typename Ty,typename... Ar>
#define Tp template<typename Ty>
#define isdigit(c) ((c)>='0'&&(c)<='9')
#define ll long long
#define RS register
#define gc getchar
#define pc putchar
#define I inline
using namespace std;
Tp I Ty wmax(Ty a,Ty b){return a>=b? a:b;}
Tp I Ty wmin(Ty a,Ty b){return a<=b? a:b;}
namespace WrongIO
{
	Tp I void read(Ty &x){x=0;Ty opt=1;char c=gc();while(!isdigit(c)&&c!='-')c=gc();if(c=='-')opt=-1,c=gc();while(isdigit(c))x=(x<<3)+(x<<1),x+=c-'0',c=gc();x*=opt;return;}
	Tp I void write(Ty x){short OI_USE[50],OI_top=0;if(x<=0) if(x==0)pc('0');else pc('-'),x*=-1;while(x)OI_USE[++OI_top]=x%10,x/=10;while(OI_top--)pc(OI_USE[OI_top+1]+'0');return;}
    I void writec(char c[]){int len=strlen(c);for(int i=0;i<len;i++)pc(c[i]);}
    I void writes(string s){int len=s.length();for(int i=0;i<len;i++)pc(s[i]);}
    I void readc(char &c,int l,int r){c=gc(); while(c!=EOF&&(c<l||c>r)) c=gc();}
    I void readc(char &c,char val){c=gc();while(c!=EOF&&c!=val) c=gc();}
    I void readc(char val){char c;c=gc();while(c!=EOF&&c!=val) c=gc();}
    I void readls(string &s){char c=gc();while(c!='\n') s.push_back(c),c=gc();}
    Ts I void read(Ty &x,Ar &...y) {read(x),read(y...);}
} using namespace WrongIO;
ll n,e[200050],ans=1e12;
ll zs[1000050],tp;
void check(ll vl)
{
	ll ta=0;
	for(int i=1;i<=n;i++)
	{
		if(e[i]<vl) ta+=vl-e[i];
		else ta+=wmin(e[i]%vl,vl-(e[i]%vl));
	}
	if(ta<ans) ans=ta;
	return;
}
void Work(ll x)
{
	bool flag=1;
	for(int j=2;j<=sqrt(x);j++)
	{
		if(x%j==0)
		{
			flag=0;
			break;
		}
	}
	if(flag==1&&x!=1) check(x);
	for(int i=1;i<=tp;i++)
	{
		if(zs[i]>x) return;
		x%zs[i]==0&&(check(zs[i]),0);
	}
	return;
}
int main()
{

	read(n);
	for(int i=1;i<=n;i++) read(e[i]);
	for(int i=2;i<=1000001;i++)
	{
		bool flag=1;
		for(int j=2;j<=sqrt(i);j++)
		{
			if(i%j==0)
			{
				flag=0;
				break;
			}
		}
		if(flag==1) zs[++tp]=i;
	}
	ll ED=25; srand(43543);
	for(int i=1;i<=ED;i++)
	{
		ll ez=rand()%n+1;
		ll ee=e[ez];
		ee!=1&&(Work(ee-1),0);
		Work(ee);
		Work(ee+1);
	}
	write(ans);
	return 0;
}

以及给出巨佬兼命题人的cxr的超短代码
#include<bits/stdc++.h>
#define Tp template<typename Ty>
#define Ts template<typename Ty,typename... Ar>
#define Reg register
#define RI Reg int
#define Con const
#define CI Con int&
#define I inline
#define W while
#define N 200000
#define V 1000000
#define LL long long
using namespace std;
int n;LL a[N+5],p[N*30];set<LL> vis;
namespace FastIO
{
	#define FS 100000
	#define tc() (FA==FB&&(FB=(FA=FI)+fread(FI,1,FS,stdin),FA==FB)?EOF:*FA++)
	char oc,FI[FS],*FA=FI,*FB=FI;
	Tp I void read(Ty& x) {x=0;W(!isdigit(oc=tc()));W(x=(x<<3)+(x<<1)+(oc&15),isdigit(oc=tc()));}
	Ts I void read(Ty& x,Ar&... y) {read(x),read(y...);}
}using namespace FastIO;
int Pt,P[V+5];I void Sieve() {for(RI i=2,j;i<=V;++i)
	for(!P[i]&&(P[++Pt]=i),j=1;j<=Pt&&i*P[j]<=V;++j) if(P[i*P[j]]=1,!(i%P[j])) break;}
int ans;I void Check(Con LL& x)
{
	if(vis.find(x)!=vis.end()) return;vis.insert(x);
	LL res=0;for(RI i=1;i<=n&&res<ans;++i) res+=a[i]<x?x-a[i]:min(a[i]%x,x-a[i]%x);
	ans>res&&(ans=res);
}
I void Work(LL x)
{
	for(RI i=1;i<=Pt&&P[i]<=x;++i) if(!(x%P[i])) {Check(P[i]);W(!(x%P[i])) x/=P[i];}
	x^1&&(Check(x),0);
}
int main()
{
	RI T,i;LL x;for(srand(998244353),Sieve(),read(n),i=1;i<=n;++i) read(a[i]);
	for(ans=n,T=1;T<=30;++T) x=a[1ull*rand()*rand()*rand()%n+1],Work(x),Work(x+1),x^1&&(Work(x-1),0);
	return printf("%d\n",ans),0;
}

蒟蒻的第一遍题解,全是文字不是还有两张题面吗,非常不好

给上样例
输入1
3
1 3 9
输出1
2

输入2
3
1001 1999 3000
输出2
2

样例3(太大了)
放不下