【洛谷5316】恋恋的数学题(数学)
大致题意: 乱序给出\(k\)个数两两\(gcd\)和\(lcm\),让你求出这\(k\)个数。
前言
比赛时看到这题心想神仙题,然后发现\(2\le k\le4\)。。。
于是又想快一个小时,才差不多想到了正解。
结果写完之后少加了一句检验,就被卡掉\(2\)分,只剩\(98\)了\(2333\)。
\(k=2\)
\(k=2\)应该是比较简单的,考虑到保证有解,因此直接把给出的数输出即可。
\(k=3\)
\(k=3\)的情况略微复杂。
考虑到乱序不太好做,一个很直接的想法,我们全排列来枚举对应顺序。
则最后可以得到:
\[gcd(v_1,v_2),gcd(v_1,v_3),gcd(v_2,v_3)
\]
\[lcm(v_1,v_2),lcm(v_1,v_3),lcm(v_2,v_3)
\]
然后如果你知道一个很显然的性质,那么这题就很水了:
\[x\cdot y=gcd(x,y)\cdot lcm(x,y)
\]
也就是说,我们可以将上面的式子两两相乘得到\(v_1\cdot v_2,v_1\cdot v_3,v_2\cdot v_3\)的值。(注意相乘会爆\(long\ long\),因此需要开\(\_\_int128\))
然后,如果我们求出\(v_1\cdot v_2,v_1\cdot v_3\)的\(gcd\),则它就相当于是\(v_1\cdot gcd(v_2,v_3)\)。
因此,我们用\(gcd(v_1\cdot v_2,v_1\cdot v_3)\)去除以\(gcd(v_2,v_3)\),就得到了\(v_1\)的值!
而\(v_2\)和\(v_3\)既然已知与\(v_1\)的乘积,就可以直接通过除法算出。
注意在枚举到的一种排列中,有许多无解的情况需要特判,列举如下:
- 两个数对应的\(lcm\)不能整除这两个数对应的\(gcd\)。
- \(gcd(v_1\cdot v_2,v_1\cdot v_3)\)不能整除\(gcd(v_2,v_3)\)。
- 最后算出的\(v_2\cdot v_3\)不等于\(gcd(v_2,v_3)*lcm(v_2,v_3)\)。
- 最后算出的\(gcd(v_1,v_2),gcd(v_1,v_3),gcd(v_2,v_3)\)与已知数据不符(我就是少判了这个丢了\(2\)分)。
\(k=4\)
知道了\(k=3\),\(k=4\)其实只要在此基础上加一些细节就可以了。
这次我们需要全排列套全排列,才能使得最后的对应顺序恰好为:
\[gcd(v_1,v_2),gcd(v_1,v_3),gcd(v_1,v_4),gcd(v_2,v_3),gcd(v_2,v_4),gcd(v_3,v_4)
\]
\[lcm(v_1,v_2),lcm(v_1,v_3),lcm(v_1,v_4),lcm(v_2,v_3),lcm(v_2,v_4),lcm(v_3,v_4)
\]
接下来,我们可以像前面一样先求出\(v_1,v_2,v_3\)的值,然后计算出\(v_4\)的值并检验即可。
具体实现详见代码。
代码
#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 LL long long
using namespace std;
int n,m;__int128 a[10],b[10];
Tp I void read(Ty& x)//读入__int128
{
char c;x=0;W(!isdigit(c=getchar()));
W(x=(x<<3)+(x<<1)+(c&15),isdigit(c=getchar()));
}
Tp I Ty gcd(Con Ty& x,Con Ty& y) {return y?gcd(y,x%y):x;}//求gcd
class Dfser_for_Three
{
private:
bool vis[10];__int128 s[10];
I bool Calc()//求答案
{
__int128 v1,v2,v3;if(gcd(a[1]*s[1],a[2]*s[2])%a[3]) return 0;//判无解
v1=gcd(a[1]*s[1],a[2]*s[2])/a[3],v2=a[1]*s[1]/v1,v3=a[2]*s[2]/v1;//求出v1,v2,v3
if(v2*v3!=a[3]*s[3]||gcd(v1,v2)^a[1]||gcd(v1,v3)^a[2]||gcd(v2,v3)^a[3]) return 0;//判无解
return printf("%lld %lld %lld\n",(LL)v1,(LL)v2,(LL)v3),1;//输出答案
}
public:
I bool dfs(CI x)//全排列
{
if(x>3) return Calc();
for(RI i=1;i<=3;++i) if(!vis[i]&&!(b[i]%a[x]))//剪枝
{
if(vis[i]=1,s[x]=b[i],dfs(x+1)) return vis[i]=0,1;
vis[i]=0;
}return 0;
}
}D3;
class Dfser_for_Four
{
private:
bool vis1[10],vis2[10];__int128 s1[10],s2[10];
I bool Calc()//求答案,与上面类似
{
__int128 v1,v2,v3,v4;if(gcd(s1[1]*s2[1],s1[2]*s2[2])%s1[4]) return 0;
v1=gcd(s1[1]*s2[1],s1[2]*s2[2])/s1[4],v2=s1[1]*s2[1]/v1,v3=s1[2]*s2[2]/v1;//求出v1,v2,v3
if(s1[3]*s2[3]%v1) return 0;v4=s1[3]*s2[3]/v1;//求出v4
if(v2*v3!=s1[4]*s2[4]||v2*v4!=s1[5]*s2[5]||v3*v4!=s1[6]*s2[6]) return 0;//判无解
if(gcd(v1,v2)^s1[1]||gcd(v1,v3)^s1[2]||gcd(v1,v4)^s1[3]) return 0;//判无解
if(gcd(v2,v3)^s1[4]||gcd(v2,v4)^s1[5]||gcd(v3,v4)^s1[6]) return 0;//判无解
return printf("%lld %lld %lld %lld\n",(LL)v1,(LL)v2,(LL)v3,(LL)v4),1;//输出答案
}
I bool dfs2(CI x)//第二层全排列
{
if(x>6) return Calc();
for(RI i=1;i<=6;++i) if(!vis2[i]&&!(b[i]%s1[x]))
{
if(vis2[i]=1,s2[x]=b[i],dfs2(x+1)) return vis2[i]=0,1;
vis2[i]=0;
}return 0;
}
public:
I bool dfs1(CI x)//第一层全排列
{
if(x>6) return dfs2(1);
for(RI i=1;i<=6;++i) if(!vis1[i])
{
if(vis1[i]=1,s1[x]=a[i],dfs1(x+1)) return vis1[i]=0,1;
vis1[i]=0;
}return 0;
}
}D4;
int main()
{
RI Ttot,i;cin>>Ttot>>n,m=n*(n-1)>>1;W(Ttot--)
{
for(i=1;i<=m;++i) read(a[i]);for(i=1;i<=m;++i) read(b[i]);
if(n==2) {printf("%lld %lld\n",(LL)a[1],(LL)b[1]);continue;}//对于两个数直接输出
if(n==3) {D3.dfs(1);continue;}D4.dfs1(1);
}return 0;
}
待到再迷茫时回头望,所有脚印会发出光芒