首页 > 其他分享 >你省(福建)省队集训模拟赛题解

你省(福建)省队集训模拟赛题解

时间:2023-07-16 22:46:43浏览次数:43  
标签:p1 return int 题解 long inline 集训 省队 define

Day5

$\texttt{T1}$

简要题意

有两个正整数 \(a<b\le 10^9\),给出 \(\dfrac{a}{b}\) 的小数点后 \(19\) 位,要求还原 \(a,b\),保证有解。

solution

一个科技:\(\texttt{Stern-Brocot tree}(SBT)\),可以参考这个博客学习。

先给出 $O(n)$ 找的代码
#include<bits/stdc++.h>
#define LL long long
#define LD long double
#define int LL
#define fr(x) freopen(#x".in","r",stdin);freopen(#x".out","w",stdout);
using namespace std;
const LD eps=1e-19;
int A,B;
#define Iabs(x) ((x)>0?(x):-(x))
#define C (x-(LD)(m)/n)
void find(LD x,int a=0,int b=1,int c=1,int d=0)
{
	int m=a+c,n=b+d;
	if(Iabs(C)<eps) return A=m,B=n,void();
	if(x<(LD)(m)/n) return find(x,a,b,m,n);
	return find(x,m,n,c,d);
}
signed main()
{
	ios::sync_with_stdio(0);cin.tie(0);cout.tie(0);LD x;cin>>x;find(x);cout<<A<<" "<<B;
	return 0;
}

注意到博客中的一句话:称从 \(SBT\) 上任意一个节点 \(\dfrac{a}{b}\) 沿着相同方向跳到需要换方向的第一个点为一次跳跃,那么该点到根节点至多跳跃 \(O(\log\max(a,b))\) 次。

于是跳的过程中二分优化一下,复杂度 \(O(\log^2 V)\)。

$\texttt{code}$
#include<bits/stdc++.h>
#define LL long long
#define LD long double
#define int LL
#define fr(x) freopen(#x".in","r",stdin);freopen(#x".out","w",stdout);
using namespace std;
const LD eps=1e-19;
int A,B;
#define Iabs(x) ((x)>0?(x):-(x))
#define C (x-(LD)(m)/n)
void find(LD x,int a=0,int b=1,int c=1,int d=0)
{
	int m=a+c,n=b+d,l=1,r=1e9,mid,ans=-1;
	if(Iabs(C)<eps) return A=m,B=n,void();
	if(x<(LD)(m)/n)
	{
		while(l<=r)
		{
			int mid=(l+r)>>1;m=c+mid*a,n=d+mid*b;
			if(C>=-eps) ans=mid,r=mid-1;else l=mid+1;
		}ans--;
		return find(x,a,b,c+ans*a,d+ans*b);
	}
	while(l<=r)
	{
		int mid=(l+r)>>1;m=a+mid*c,n=b+mid*d;
		if(C<=eps) ans=mid,r=mid-1;else l=mid+1;
	}ans--;
	return find(x,a+ans*c,b+ans*d,c,d);
}
signed main()
{
	ios::sync_with_stdio(0);cin.tie(0);cout.tie(0);LD x;cin>>x;find(x);cout<<A<<" "<<B;
	return 0;
}

$\texttt{T3}$

简要题意

有 \(1\le T\le 10^6\) 次询问,每次询问正整数 \(x,p\),\(p\) 为素数,令 \(n=xp^2\le 10^{18}\),问是否存在三个正整数 \(a,b,c\),满足 \(ab+bc+ca=n\)。有的话给出构造,否则输出 \(-1\) 。

solution

打表注意到只有 \(n=4,18\) 是无解的。

打表
namespace DB
{
	const int N=1e5;
	struct node{int x,y,z;}db[N+5];
	inline void init()
	{
		for(int i=1;i<=N;i++)
		{
			bool ok=1;
			for(int a=1;a*a<=i;a++)
			{
				for(int b=1;b*b<=i;b++)
				{
					int t=i-a*b;if(t<=0) continue;
					if(t%(a+b)==0){db[i]={a,b,t/(a+b)};ok=0;break;}
				}
				if(!ok) break;
			}
		}
	}
}using DB::db;

这里尽管理论复杂度是 \(O(N^2)\) 的,但是实际跑 \(10^5\) 大约 \(0.2s\) 不到。我大概跑了 \(N=10^6\) 验证只有那两个无解。


下面做法不依赖与 \(n=xp^2\)

考虑钦定其中一个数 \(c\) 为 \(r\),两边同时加 \(r^2\) 得:\((r+a)(r+b)=n+r^2\)

考虑找一个素数 \(q\mid n+r^2\),发现 \(\lceil\) 是否存在 \(r\) 满足条件 \(\rfloor\) 这是一个二次剩余问题。我们不妨假设我们已经会快速求二次剩余了(后面其实不需要)。

此时,我们还需满足 \(a,b>0\),即 \(q>r,\dfrac{n+r^2}{q}>r\)。首先 \(q>r\) 是容易满足的。

而 \(\dfrac{n+r^2}{q}>r\Leftarrow \dfrac{n+r^2}{q}>q\Leftarrow n+r^2>q^2\Leftarrow q^2<n\)。

由于我们预处理了 \(n\le 10^5\) 的情况,于是我们只能猜测在 \(q\le 300\) 以内能找到满足 \(-n\) 是模 \(q\) 的二次剩余,设素数为 \(q\),二次剩余为 \(r\)。

此时 \(a=q-r,b=\dfrac{n+r^2}{q}-r\),于是我们找到了解。


再来解决上面的两个遗留问题。

  1. 快速求二次剩余,这里我考场上做得很不光明正大,是贺板子的,后面不想改了。优秀做法:发现只要对 \(300\) 以内素数求二次剩余,预处理即可。
  2. 猜测在 \(q\le 300\) 以内能找到满足 \(-n\) 是模 \(q\) 的二次剩余。随机一个数是模 \(p\) 的二次剩余的概率大概为 \(\dfrac{1}{2}\),于是大胆猜想在 \(300\) 内都没找到的概率很小很小。如果有数竞强神证证就好了。

于是我就通过过了这道题。复杂度 \(O(T)\),常数 \(60\) 左右,实际在随机情况下跑得飞快,出题人很懒,也是随机造数据。


考场代码
#include<bits/stdc++.h>
#define P pair<int,int>
#define fi first
#define se second
#define LL long long
#define int LL
#define fr(x) freopen(#x".in","r",stdin);freopen(#x".out","w",stdout);
using namespace std;
namespace IO
{
	const int _Pu=21e7+5,_d=32;
	char buf[_Pu],obuf[_Pu],*p1=buf+_Pu,*p2=buf+_Pu,*p3=obuf,*p4=obuf+_Pu-_d;
	inline void fin()
	{
		memmove(buf,p1,p2-p1);
		int rlen=fread(buf+(p2-p1),1,p1-buf,stdin);
		if(p1-rlen>buf) buf[p2-p1+rlen]=EOF;p1=buf;
	}
	inline void fout(){fwrite(obuf,p3-obuf,1,stdout),p3=obuf;}
	inline int rd()
	{
		if(p1+_d>p2) fin();int isne=0,x=0;
		for(;!isdigit(*p1);++p1) isne=(*p1=='-');x=(*p1++-'0');
	    for(;isdigit(*p1);++p1) x=x*10+(*p1-'0');
		if(isne) x=-x;return x;
	}
	inline void wr(int x,char end='\n')
	{
		if(!x) return *p3++='0',*p3++=end,void();
		if(x<0) *p3++='-',x=-x;
		char sta[20],*top=sta;
		do{*top++=(x%10)+'0';x/=10;}while(x);
		do{*p3++=*--top;}while(top!=sta);(*p3++)=end;
	}
}//快读(贺的板子),注意 _Pu 一定要开大,否则会少读/少输 
LL T,x,p,n;
const LL un[]={1,2,4,6,10,18,22,30,42,58,70,78,102,130,190,210,330,462};//事实上只有 4,18 是 xp^2 型 
namespace DB
{
	const int N=1e5;
	struct node{int x,y,z;}db[N+5];
	inline void init()
	{
		for(int i=1;i<=N;i++)
		{
			bool ok=1;
			for(int a=1;a*a<=i;a++)
			{
				for(int b=1;b*b<=i;b++)
				{
					int t=i-a*b;if(t<=0) continue;
					if(t%(a+b)==0){db[i]={a,b,t/(a+b)};ok=0;break;}
				}
				if(!ok) break;
			}
		}
	}
}using DB::db;//打表 
namespace er
{
    const int pr[]={3,5,7,11,13,17,19,23,29,31,37,41,43,47,53,59,61,67,71,73,79,83,89,97,101,103,107,109,113,127,131,137,139,149,151,157,163,167,173,179,181,191,193,197,199,211,223,227,229,233,239,241,251,257,263,269,271,277,281,283,293};
    int n,mod,w;
    struct comp
    {
        int x,y;
        inline comp operator *(comp X)
        {
            return {(1ll*x*X.x%mod+1ll*y*X.y%mod*w%mod)%mod,
            (1ll*x*X.y%mod+1ll*y*X.x%mod)%mod};
        }
    };
    inline int ksm(int x,int p)
    {
        int s=1;
        for(;p;(p&1)&&(s=1ll*s*x%mod),x=1ll*x*x%mod,p>>=1);
        return s;
    }
    inline comp ksm1(comp x,int p)
    {
        comp s={1,0};
        for(;p;(p&1)&&(s=s*x,1),x=x*x,p>>=1);
        return s;
    }
    inline int Find()
    {
        int x;
        while(1)
        {
            x=rand()%mod;w=((LL)x*x%mod-n+mod)%mod;
            if(ksm(w,(mod-1)>>1)==mod-1) break;
        }
        return ksm1({x,1},(mod+1)>>1).x;
    }
    inline P fer(int a)
    {
        for(int i=0;i<60;i++)
        {
            mod=pr[i];n=mod-a%mod;if(!n||n==mod) continue;
            if(ksm(n,(mod-1)>>1)==mod-1) continue;
            int ans=Find();return {mod,ans};
        }
        return {-1,-1};
    }
}using er::fer;//不光明正大的地方,你们可以把这里改成预处理300以内二次剩余 
signed main()
{
//	fr(construct)
	T=IO::rd();DB::init();
	while(T--)
	{
		x=IO::rd(),p=IO::rd();n=x*p*p;bool ok=1;
		for(int i=0;i<18;i++) if(n==un[i]){ok=0;break;}//自己这里判断改一下 
		if(!ok){IO::wr(-1);continue;}
		if(n&1){IO::wr(1,' ');IO::wr(1,' ');IO::wr((n-1)/2);continue;}//防止挂分特判 
		if(n%4==0){IO::wr(2,' ');IO::wr(2,' ');IO::wr((n-4)/4);continue;}//防止挂分特判 
		if(n<=1e5){IO::wr(db[n].x,' ');IO::wr(db[n].y,' ');IO::wr(db[n].z);continue;}
		P t=fer(n);int q=t.fi,r=t.se;r=min(r,q-r);int Q=(n+r*r)/q;
		int a=q-r,b=Q-r;
		IO::wr(a,' ');IO::wr(b,' ');IO::wr(r);
	}
	return IO::fout(),0;
}

后记

考场上写完这题拍了好久就摆烂了,发现最后 \(\texttt{rk14}\),是这几次最好,很开心。也有很多大佬切了这题,卷王,\(Linshey\) 两位 \(\texttt{AK}\),珂怕。

正解是个很傻逼的结论,参考 oeis,实际上就是把 \(\texttt{un}\) 数组放进去搜。发现我这乱搞还是很优秀的,可以看做正解。感受那股劲!

Day6

$\texttt{T3}$

solution

首先,随机化、退火、贪心找规律等乱搞我们在考场上拿了 \([50,60]\) 的分,还是很高的。

考虑观察到一件事情:我们只需对于所有素数 \(p\),确定 \(a_p\) 即可确定所有 \(a_i\)。

由于 \(a_i\) 完全积性,我们想到 \(\texttt{Jacobi}\) 符号,即考虑 \(\left(\dfrac{i}{3}\right)\),我们令 \(a_3=-1,a_p=\left(\dfrac{p}{3}\right)(p\neq 3)\),这样得到的 \(a\) 数列就有一个为 \(3\) 的循环节(不严谨,但你可以这样理解)。

于是这样的构造就比较优秀,实际 \(S=7,85pts\),其中 \(a_3\gets 1\) 经测试不会使答案更优秀。

考虑在此基础上进行调整,对于前缀和的绝对值过大的情况,通过修改之前质数处的取值以控制 \(S\) 范围,可以做到 \(S = 5\)。

这句话是 \(Renshey\) 题解里的说法。我具体来说:

inline bool chk(){for(int i=1;i<=n-70;i++) if(abs(s[i+70]-s[i])>6) return 0;return 1;}
	while(1)
	{
		sol();int s=0,w=-1;
		for(int i=1;i<=n;i++)
		{
			s+=a[i];
			if(abs(s)>4/*or5*/){w=i;break;}
		}
		if(w==-1) break;
		for(int i=w;i;i--) if(!d[i]&&a[i]==sig(::s[w]))
		{
			a[i]=-a[i];sol();
			if(chk()) break;a[i]=-a[i];
		}
	}

至于为啥我也不知道,反正这样调是对的。而且能做到让 \(S=4\),如果要 \(S=5\) 把代码中 \(4\) 改成 \(5\) 即可。其中 \(4\) 跑了 \(12s\),\(5\) 跑了不到 \(1s\)。

构造的代码
#include<bits/stdc++.h>
#define LL long long
#define fr(x) freopen(#x".in","r",stdin);freopen(#x".out","w",stdout);
using namespace std;
const int n=1e6;
int a[n+5],s[n+5],d[n+5],pr[n+5];
#define sig(x) ((x)>0?1:-1)
inline void sol(){for(int i=2;i<=n;i++){if(d[i]) a[i]=a[i/d[i]]*a[d[i]];s[i]=s[i-1]+a[i];}}
inline bool chk(){for(int i=1;i<=n-70;i++) if(abs(s[i+70]-s[i])>6) return 0;return 1;}
inline int f (int S)
{
	if (S >= 100000) return ceil(1.0 * (1100000 - S) / 100000);
	if (S >= 10000) return ceil(1.0 * (190000 - S) / 9000);
	if (S >= 1000) return ceil(1.0 * (28000 - S) / 900);
	if (S >= 500) return ceil(1.0 * (2500 - S) / 50);
	if (S >= 100) return ceil(1.0 * (4700 - 3 * S) / 80);
	if (S >= 17) return ceil(100 * pow(S, -0.5) + 45);
	if (S >= 7) return ceil(100 * pow(log2(S) / 2, -0.5));
	if (S == 6) return 92;
	return 100;
}
inline void checkans()
{
	int s=0,mx=0;
	for(int i=1;i<=n;i++)
	{
		if(abs(a[i])!=1) return cerr<<"WA!a["<<i<<"]"<<" is not equal 1 or -1!",void();
		s+=a[i],mx=max(mx,abs(s));
	}
	for(int i=1;i<=n;i++) for(int j=i;j<=n;j+=i) if(a[j]!=a[i]*a[j/i]) return cerr<<"WA!a["<<i<<"]*a["<<j/i<<"] is not equal a["<<j<<"]!",void();
	int fen=f(mx);
	if(fen==100) cerr<<"AC!";
	else cerr<<"points = "<<fen<<"!";
}//假设你把答案数组放到a数组,编号从1开始,调用checkans()片段能帮助你 判断答案是否符合条件 and 查看你获得的分数
int main()
{
	freopen("occultism.out","w",stdout);
	for(int i=2;i<=n;i++) for(int j=2*i;j<=n;j+=i) if(!d[j]) d[j]=i;a[1]=s[1]=1;
	for(int i=2;i<=n;i++)
	{
		if(!d[i]) a[i]=i%3==1?1:-1,pr[++pr[0]]=i;
		else a[i]=a[i/d[i]]*a[d[i]];s[i]=s[i-1]+a[i];
	}
	while(1)
	{
		sol();int s=0,w=-1;
		for(int i=1;i<=n;i++)
		{
			s+=a[i];
			if(abs(s)>4/*or5*/){w=i;break;}
		}
		if(w==-1) break;
		for(int i=w;i;i--) if(!d[i]&&a[i]==sig(::s[w]))
		{
			a[i]=-a[i];sol();
			if(chk()) break;a[i]=-a[i];
		}
	}
	checkans();
	for(int i=1;i<=n;i++) (a[i]<0)&&(putchar('-')),putchar('1'),putchar(' ');
	return 0;
}

后记

考场上乱搞我是 \(59pts\),当时想过素数模 \(3\) 了,但是看打的表模 \(3\) 没有规律就没尝试了。还是应该勇于尝试的。

标签:p1,return,int,题解,long,inline,集训,省队,define
From: https://www.cnblogs.com/HaHeHyt/p/17558745.html

相关文章

  • 洛谷-P9455 题解
    写在前面:本题蒟蒻给出两种做法,一种乱搞贪心(只是目前能过,若能被Hack请和我说),一种正解二分。正文1最坏时间复杂度:\(\mathcal{O}(n+\logV)(V=10^9)\)这个做法是很简单的,在此不多讲。只需要二分超频电压mid即可,若当前mid可行,则令右边界缩小至mid,否则令左边界扩大至mid。......
  • 230226题解
    A数列题目描述给定一个长为\(n\)的数列\(A_1,A_2,…,A_n\)。给出\(q\)次询问,每次询问给定\(X\),请你回答至少需要多少次操作,能够让数列中的每个数都变成\(X\)。每次操作你可以选择数列中的一个数加\(1\)或者减\(1\)。询问之间相互独立。输入格式第一行两个整数\(n,q\)。第......
  • 题解 HDU5726【GCD】/ LGT353762【Soso 的最大公约数】
    Problem给你一个长为\(N(1\leqN\leq1\times10^5)\)的整数序列:\(a_{1},\cdots,a_{n}(0<a_{i}\leq1\times10^9)\)。有\(Q(1\leqQ\leq1\times10^5)\)次提问。每次提问有个范围\(l,r\),你需要计算出\(\gcd(a_{l},,a_{l+1},...,a_{r})\),并且统计数对\((l’,r’......
  • Codeforces Round 896 Div2 A-D题解
    CodeforcesRound896A.Politics这题问的是,给一些人的在n个议题的观点,然后你可以随意安排顺序,每个议题人多的赢,反对派会离开,问随便安排议题,最多留下多少人,包括我自己这个题刚开始愣了半天,但是想到,那只要把所有和我观点一致的给留下来不就行了???然后交上去就AC了ACCode#inclu......
  • 题解 CF1842H【Tenzing and Random Real Numbers】
    看了题解。好难受,想用积分求概率,算了半天。发现没啥规律,不是不能算,就是太可怕了。Problem有\(n\)个\([0,1]\)范围内的均匀随机变量\(x_{1\cdotsn}\)和\(m\)条限制,每条限制形如\(x_i+x_j\le1\)或\(x_i+x_j\ge1\)。请你求出所有限制均满足的概率。对\(998244353\)......
  • Noip优质模拟赛口胡题解
    HDU5719题意概括:第一行输入t表示输入数据,每组数据第一行n,表示对1—n进行排序。接下来输入n个数b[n]表示排列中第i个数之前的最小值为b[i]。第三行n个数c[n],表示排列中第i个数之前的最大值为c[i]。解题思路:递推,排除掉6种不可能的情况,1、b[i]>b[i-1]2、c[i]<c[i-1]3、b[i]......
  • 2023.07.16 高质量 NOIP 模拟赛题解
    HDU5719Arrange【模拟】给定数列\(B_n,C_n\),求出满足\[B_i=\min_{j=1}^i\{A_j\},\quadC_i=\max_{j=1}^i\{A_j\}\]的排列\(A\)的数量。维护每个位置可能的数字数量,然后乘法原理即可。代码:http://acm.hdu.edu.cn/viewcode.php?rid=38654445。HDU5807KeepInTouch......
  • HHHOJ #1247. 「NOIP 2023 模拟赛 20230715 A」1 题解--zhengjun
    法老找来的题,说是找了三道其他模拟赛的T4拼成T1~T3,另外搞了道T4。思维好题,但是放在T1有点搞心态,但是还好大样例够强,400没挂。然而T3大样例输出错了,浪费了我0.5h,差评。首先发现向左走之后向右走是一定不优的,所以最短路的情况只能先向右再向左。考虑枚举起点\(s......
  • 2023ACM暑期集训 DAY 2
    模拟赛1题解A上班代码点击查看代码#include<bits/stdc++.h>usingnamespacestd;intx,y,z;intmain(){cin>>x>>y>>z;printf("%d",x+min(y,z));}B 崇拜代码点击查看代码#include<bits/stdc++.h>#definelllonglong#define......
  • 题解 P2839【[国家集训队] middle】
    Problem一个长度为\(n\)的序列\(a\),设其排过序之后为\(b\),其中位数定义为\(b_{n/2}\),其中\(a,b\)从\(0\)开始标号,除法下取整。给你一个长度为\(n\)的序列\(s\)。回答\(Q\)个这样的询问:\(s\)的左端点在\([a,b]\)之间,右端点在\([c,d]\)之间的子区间中,最大的中......