Link.
Description.
給定一個長度為 \(n\) 的序列,是一個 \([l,\dots,r]\) 的排列。
給定 \(n\),每次可以詢問任意不同兩個的 \(\text{lcm}\),要在 \(n+5000\) 的復雜度內問出序列。
Solution.
小數據暴力
首先有個很顯然的詢問數 \(\frac {n\cdot(n-1)}2\) 的做法。
解一下方程發現 \(\frac{n\cdot (n-1)}2=n+5000\) 的解大概是 101.511249367
。
所以有一個顯然的 \(O(n^3)\) 做法,找到最大的數,它必定是 \((r-1)\cdot r\)。
然后每次找到沒標記過的 \(i\cdot (i-1)\) 即可。
大數據隨機
我們發現在長度比較長的情況下,質數分布概率是比較高的,大概是 \(\frac 1{\log n}\ge\frac 1{20}\)。
考慮隨機 rand \(10000\) 個數,其中沒有質數的概率是 \(1-\left(\frac {19}{20}\right)^{10000}\),已經很小了。
然后我們找到了質數的情況下,可以直接去問它和任意數的 \(\text{lcm}\) 然后就可以 \(n-1\) 次詢問得到整個序列。
Attention.
- 他媽多組詢問的交互題就是屑,一個點 WA 會導致后面的 tests TLE,然后讓我以為我質因數分解太慢了,調了一年。
- 這題很牛逼,如果小數據暴力沒開到 100,就也會掛,大概就有一段長度為 \(85\) 的區間 \([155922,156006]\) 里面沒有一個質數,所以小數據暴力一定要開盡量大。
- 因為這題很顯然我們需要分解的數雖然很大,但是它 \(2,3\) 等較小數出現概率比較高,所以直接暴力分解是可以的。
Coding.
點擊查看破爛代碼
//是啊,你就是那只鬼了,所以被你碰到以后,就輪到我變成鬼了{{{
#include<bits/stdc++.h>
using namespace std;typedef long long ll;
template<typename T>inline void read(T &x)
{
x=0;char c=getchar(),f=0;
for(;c<48||c>57;c=getchar()) if(!(c^45)) f=1;
for(;c>=48&&c<=57;c=getchar()) x=(x<<1)+(x<<3)+(c^48);
f?x=-x:x;
}
template<typename T,typename...L>inline void read(T &x,L&...l) {read(x),read(l...);}/*}}}*/
const int N=1000005;int n,a[N],id[N],rs[N],vs[N];ll vl[105][105];
int pr[N],pc,ls[N],ph[N],mu[N];char pv[N];//prinit{{{
inline void prinit(int n)
{
pv[1]=mu[1]=ph[1]=1,ls[1]=0;for(int i=1;i<=n;i++)
{
if(!pv[i]) pr[++pc]=i,mu[i]=-1,ph[i]=i-1,ls[i]=i;
for(int j=1;j<=pc&&i*pr[j]<=n;j++)
{
ls[i*pr[j]]=pr[j],mu[i*pr[j]]=i%pr[j]?-mu[i]:0;
ph[i*pr[j]]=ph[i]*(pr[j]-!!(i%pr[j]));
pv[i*pr[j]]=1;if(i%pr[j]==0) break;
}
}
}//}}}//
#ifdef ONLINE_JUDGE
inline ll qry(int x,int y) {printf("? %d %d\n",x,y),fflush(stdout);ll r;return read(r),r;}
#else
inline ll qry(int x,int y) {return 1ll*a[x]*a[y]/__gcd(a[x],a[y]);}
#endif
inline void baoli()
{
#ifndef ONLINE_JUDGE
for(int i=1;i<=n;i++) read(a[i]);
#endif
ll mx=0;for(int i=1;i<=n;i++) for(int j=i+1;j<=n;j++)
mx=max(mx,vl[i][j]=vl[j][i]=qry(i,j));
int r=sqrt(mx)-3,l;r=max(r,1),l=r-n+1;while(1ll*r*(r-1)!=mx) r++,l++;
for(int i=1;i<=n;i++) rs[i]=0;
for(int nw=r;nw>l;nw--)
{
int wa,wb;for(int i=1;i<n;i++) for(int j=i+1;j<=n;j++)
if(!rs[i]&&!rs[j]&&vl[i][j]==1ll*nw*(nw-1)) wa=i,wb=j;
int wh=wa;for(int i=1;i<=n;i++) if(wa^i&&vl[wa][i]%nw) {wh=wb;break;}
rs[wh]=nw;
}
for(int i=1;i<=n;i++) if(!rs[i]) rs[i]=l;
putchar('!');for(int i=1;i<=n;i++) printf(" %d",rs[i]);
putchar('\n'),fflush(stdout);
}
inline int fac(ll x)
{
int rs=0;for(int i=1;i<=pc&&x>1000000;i++) if(x%pr[i]==0)
{rs=max(rs,pr[i]);while(x%pr[i]==0) x/=pr[i];}
while(x^1) rs=max(rs,ls[x]),x/=ls[x];
return rs;
}
inline void real()
{
#ifndef ONLINE_JUDGE
for(int i=1;i<=n;i++) read(a[i]);
#endif
int mx=0,ma=0,mb=0;ll mw=0;for(int i=1;i<=5000;i++)
{
int a=rand()%n+1,b=rand()%n+1;if(a==b) b=b%n+1;
ll mv=qry(a,b);int vl=fac(mv);if(vl>mx) mx=vl,ma=a,mb=b,mw=mv;
}
int qwq=rand()%n+1;while(qwq==ma||qwq==mb) qwq=rand()%n+1;
if(qry(qwq,ma)%mx) swap(ma,mb);
//cerr<<"debug : "<<ma<<" "<<mx<<","<<mw/mx<<"=="<<a[ma]<<endl;
rs[ma]=mx,rs[mb]=mw/mx;for(int i=1;i<=n;i++) if(i!=ma&&i!=mb) rs[i]=qry(ma,i)/mx;
putchar('!');for(int i=1;i<=n;i++) printf(" %d",rs[i]);
putchar('\n'),fflush(stdout);
}
inline void solve() {read(n);if(n<=101.511249367) return baoli();else return real();}
int main() {int Ca;for(prinit(N-1),read(Ca),srand(time(0));Ca--;) solve();return 0;}