视频讲解:BV1iL4y1Y7WU
A. The Miracle and the Sleeper
题目大意
给定两个正整数 l , r ( 1 ≤ l ≤ r ≤ 1 0 9 ) l,r~(1 \leq l \leq r \leq 10^9) l,r (1≤l≤r≤109) ,求满足 r ≥ a ≥ b ≥ l r \ge a \ge b \ge l r≥a≥b≥l 的最大的 $a \mod{b} $ 。
题解
若
l
l
l 足够小,那么
a
a
a 应该取
r
r
r ,
b
b
b 应该取
⌊
r
+
1
2
⌋
\lfloor \frac{r+1}{2} \rfloor
⌊2r+1⌋ ,答案为
⌊
r
−
1
2
⌋
\lfloor \frac{r-1}{2} \rfloor
⌊2r−1⌋ 。
若
l
l
l 较大,那么
a
=
r
,
b
=
l
a=r,b=l
a=r,b=l ,答案为
r
m
o
d
l
r\mod{l}
rmodl 。
分界点为
⌊
r
+
1
2
⌋
\lfloor \frac{r+1}{2} \rfloor
⌊2r+1⌋ 。
参考代码
#include<bits/stdc++.h>
using namespace std;
int main()
{
int T,l,r;
cin>>T;
while(T--)
{
cin>>l>>r;
if(l<=(r+1)/2)
cout<<(r-1)/2<<endl;
else
cout<<r-l<<endl;
}
}
B. Scenes From a Memory
题目大意
给定一个十进制下不包含 0 0 0 的整数 n ( 1 ≤ n < 1 0 50 ) n(1 \le n < 10^{50}) n(1≤n<1050) ,删除最多的数字,使其变为非素数。
给定数据保证有解,若有多组解则输出任意一种即可。
题解
若包含
1
,
4
,
6
,
8
,
9
1,4,6,8,9
1,4,6,8,9 之一,则直接输出一位数即可。
若包含
2
,
5
2,5
2,5 之一且不为首位,则直接输出首位与
2
2
2 或
5
5
5 即可。
若包含两个
3
,
7
3,7
3,7 ,则直接输出
33
33
33 或
77
77
77 即可。
剩余只有
37
,
73
,
237
,
273
,
537
,
573
37,73,237,273,537,573
37,73,237,273,537,573 这几种情况。由于保证必定有解,因此不存在
37
,
73
37,73
37,73 这两种无解情况。对于剩余四种情况,显然
27
27
27 或
57
57
57 合法。
因此,答案必定为二位数,可以通过上述方式分类讨论,也可以直接枚举得到。
参考代码
#include<bits/stdc++.h>
using namespace std;
int main()
{
int T,sum[15],pos[15],i,j,k,flag;
char s[55];
scanf("%d",&T);
while(T--)
{
scanf("%d",&k);
scanf("%s",&s);
memset(sum,0,sizeof(sum));
for(i=0;i<k;i++)
{
sum[s[i]-'0']++;
pos[s[i]-'0']=i;
}
if(sum[1])
printf("1\n1\n");
else if(sum[4])
printf("1\n4\n");
else if(sum[6])
printf("1\n6\n");
else if(sum[8])
printf("1\n8\n");
else if(sum[9])
printf("1\n9\n");
else if(sum[2]&&pos[2]!=0)
printf("2\n%c2\n",s[0]);
else if(sum[5]&&pos[5]!=0)
printf("2\n%c5\n",s[0]);
else if(sum[3]>1)
printf("2\n33\n");
else if(sum[7]>1)
printf("2\n77\n");
else
{
flag=1;
for(i=0;i<k&&flag;i++)
{
for(j=i+1;j<k;j++)
{
if((s[i]-'0'+s[j]-'0')%3==0)
{
printf("2\n%c%c\n",s[i],s[j]);
flag=0;
break;
}
}
}
}
}
}
C. Rings
题目大意
有一个长度为 n ( 2 ≤ n ≤ 2 ⋅ 1 0 4 ) n~(2 \leq n \leq 2\cdot 10^4) n (2≤n≤2⋅104) 仅由01构成的字符串 s s s 。
定义函数 f f f 表示将字符串视为二进制数后再转为十进制数的结果,例如 f ( 001010 ) = 10 f(001010)=10 f(001010)=10 。
你需要找到满足以下条件的两对整数 ( l 1 , r 1 ) , ( l 2 , r 2 ) (l_1,r_1),(l_2,r_2) (l1,r1),(l2,r2) :
- 1 ≤ l 1 ≤ n , 1 ≤ r 1 ≤ n , r 1 − l 1 + 1 ≥ ⌊ n 2 ⌋ 1 \leq l_1 \leq n,1 \leq r_1 \leq n,r_1-l_1+1\geq \lfloor \frac{n}{2} \rfloor 1≤l1≤n,1≤r1≤n,r1−l1+1≥⌊2n⌋
- 1 ≤ l 2 ≤ n , 1 ≤ r 2 ≤ n , r 2 − l 2 + 1 ≥ ⌊ n 2 ⌋ 1 \leq l_2 \leq n,1 \leq r_2 \leq n,r_2-l_2+1\geq \lfloor \frac{n}{2} \rfloor 1≤l2≤n,1≤r2≤n,r2−l2+1≥⌊2n⌋
- 存在非负整数 k k k ,满足 f ( s [ l 1 : r 1 ] ) = f ( s [ l 2 : r 2 ] ) ⋅ k f(s[l_1:r_1])=f(s[l_2:r_2])\cdot k f(s[l1:r1])=f(s[l2:r2])⋅k
题解
由于是二进制下截取子串,因此考虑
k
=
2
k=2
k=2 的情况。
若
∃
i
∈
[
⌊
n
2
⌋
+
1
,
n
]
,
s
[
i
]
=
0
\exist i\in[\lfloor \frac{n}{2} \rfloor+1,n],s[i]=0
∃i∈[⌊2n⌋+1,n],s[i]=0 则存在合法情况
(
l
1
,
r
1
)
=
(
1
,
i
)
,
(
l
2
,
r
2
)
=
(
1
,
i
−
1
)
,
k
=
2
(l_1,r_1)=(1,i),(l_2,r_2)=(1,i-1),k=2
(l1,r1)=(1,i),(l2,r2)=(1,i−1),k=2 。
当不存在上述
i
i
i 时,则表示
s
s
s 后半段全为
1
1
1 。
若
s
[
⌊
n
2
⌋
]
=
0
s[\lfloor \frac{n}{2} \rfloor]=0
s[⌊2n⌋]=0 ,则存在合法情况
(
l
1
,
r
1
)
=
(
⌊
n
2
⌋
,
n
)
,
(
l
2
,
r
2
)
=
(
⌊
n
2
⌋
+
1
,
n
)
,
k
=
1
(l_1,r_1)=(\lfloor \frac{n}{2} \rfloor,n),(l_2,r_2)=(\lfloor \frac{n}{2} \rfloor+1,n),k=1
(l1,r1)=(⌊2n⌋,n),(l2,r2)=(⌊2n⌋+1,n),k=1 。
若
s
[
⌊
n
2
⌋
]
=
1
s[\lfloor \frac{n}{2} \rfloor]=1
s[⌊2n⌋]=1 ,则存在合法情况
(
l
1
,
r
1
)
=
(
⌊
n
2
⌋
,
n
−
1
)
,
(
l
2
,
r
2
)
=
(
⌊
n
2
⌋
+
1
,
n
)
,
k
=
1
(l_1,r_1)=(\lfloor \frac{n}{2} \rfloor,n-1),(l_2,r_2)=(\lfloor \frac{n}{2} \rfloor+1,n),k=1
(l1,r1)=(⌊2n⌋,n−1),(l2,r2)=(⌊2n⌋+1,n),k=1 。
参考代码
#include<bits/stdc++.h>
using namespace std;
const int MAXN=20020;
char s[MAXN];
int main()
{
int T,n,flag,i;
scanf("%d",&T);
while(T--)
{
scanf("%d",&n);
scanf("%s",s+1);
flag=0;
for(i=n/2+1;i<=n;i++)
{
if(s[i]=='0')
{
printf("%d %d %d %d\n",1,i,1,i-1);
flag=1;
break;
}
}
if(!flag)
{
if(s[n/2]=='0')
printf("%d %d %d %d\n",n/2,n,n/2+1,n);
else
printf("%d %d %d %d\n",n/2,n-1,n/2+1,n);
}
}
}
D1+D2. Two Hundred Twenty One
题目大意
有一个长度为 n ( 1 ≤ n ≤ 3 ⋅ 1 0 5 ) n~(1 \leq n \leq 3 \cdot 10^5) n (1≤n≤3⋅105) 的仅由±组成的字符串 s s s ,其中+可以视为 1 1 1 ,-可以视为 − 1 -1 −1 。字符串合法的条件是奇数位之和等于偶数位之和,即 ∑ i = 1 n ( − 1 ) i − 1 s i = 0 \sum_{i=1}^n(-1)^{i-1}s_i=0 ∑i=1n(−1)i−1si=0 。
有 q ( 1 ≤ q ≤ 3 ⋅ 1 0 5 ) q(1 \leq q \leq 3 \cdot 10^5) q(1≤q≤3⋅105) 次询问,每次给定区间 [ l , r ] ( 1 ≤ l ≤ r ≤ n ) [l,r]~(1 \leq l \leq r \leq n) [l,r] (1≤l≤r≤n) ,求最少需要删除多少字符,才能使得子串 s [ l , r ] s[l,r] s[l,r] 合法。
对于Easy Version,只需输出删除数量。
对于Hard Version,还需输出删除方案。
题解
定义字符串
s
s
s 的权值为
∑
i
=
1
∣
s
∣
(
−
1
)
i
−
1
s
i
\sum_{i=1}^{|s|}(-1)^{i-1}s_i
∑i=1∣s∣(−1)i−1si
设
s
u
m
i
sum_i
sumi 表示子串
s
[
1
,
i
]
s[1,i]
s[1,i] 的权值。
在子串
s
[
l
,
r
]
s[l,r]
s[l,r] 中删除字符
s
i
s_i
si ,则表示对
s
[
i
+
1
,
r
]
s[i+1,r]
s[i+1,r] 部分的子串权值取相反数。
由于
s
u
m
i
sum_i
sumi 在整数上连续,因此不妨设
f
(
i
)
f(i)
f(i) 表示
s
[
l
,
r
]
s[l,r]
s[l,r] 删除
s
i
s_i
si 后的权值:
f
(
i
)
=
s
u
m
i
−
1
−
s
u
m
l
−
1
−
(
s
u
m
r
−
s
u
m
i
)
f(i)=sum_{i-1}-sum_{l-1}-(sum_r-sum_i)
f(i)=sumi−1−suml−1−(sumr−sumi)
易得
f
(
i
+
1
)
−
f
(
i
)
=
s
u
m
i
+
1
−
s
u
m
i
−
1
f(i+1)-f(i)=sum_{i+1}-sum_{i-1}
f(i+1)−f(i)=sumi+1−sumi−1
因此
f
(
i
+
1
)
−
f
(
i
)
f(i+1)-f(i)
f(i+1)−f(i) 的值必定为
0
0
0 或
±
2
\pm 2
±2 。
且
f
(
l
)
=
−
s
u
m
r
+
s
u
m
l
f(l)=-sum_r+sum_l
f(l)=−sumr+suml
f ( r ) = s u m r − 1 − s u m l − 1 = − f ( l ) − s r + s l f(r)=sum_{r-1}-sum_{l-1}=-f(l)-s_r+s_l f(r)=sumr−1−suml−1=−f(l)−sr+sl
易得
f
(
r
)
+
s
r
f(r)+s_r
f(r)+sr 与
f
(
l
)
−
s
l
f(l)-s_l
f(l)−sl 互为相反数。
若
f
(
r
)
≥
2
f(r)\geq 2
f(r)≥2 则
f
(
l
)
≤
0
f(l)\leq 0
f(l)≤0 。
若
f
(
r
)
≤
−
2
f(r)\leq -2
f(r)≤−2 则
f
(
l
)
≥
0
f(l)\geq 0
f(l)≥0 。
若
f
(
r
)
=
0
f(r)=0
f(r)=0 则直接找到解。
f
(
r
)
=
±
1
f(r)=\pm 1
f(r)=±1 则无解。
因此
- 当 f ( r ) f(r) f(r) 为偶数时,由于连续函数性质,必定存在 f ( i ) = 0 f(i)=0 f(i)=0 。
- 当 f ( r ) f(r) f(r) 为奇数时,无解。
对于Easy version,
- 若 s u m r − s u m l − 1 = 0 sum_r-sum_{l-1}=0 sumr−suml−1=0 则不用删除字符。
- 若 s u m r − s u m l − 1 sum_r-sum_{l-1} sumr−suml−1 为奇数,根据 f ( i ) f(i) f(i) 的性质,必定存在 i i i 满足 s u m r − s u m i = s u m i − 1 − s u m l − 1 sum_r-sum_i=sum_{i-1}-sum_{l-1} sumr−sumi=sumi−1−suml−1 ,因此删除一个字符。
- 若 s u m r − s u m l − 1 sum_r-sum_{l-1} sumr−suml−1 为偶数,则删除任意一个字符(例如 s l s_l sl )后即可转化为前一种问题。因此删除两个字符。
对于Hard version,
我们需要寻找满足
s
u
m
r
−
s
u
m
i
=
s
u
m
i
−
1
−
s
u
m
l
−
1
sum_r-sum_i=sum_{i-1}-sum_{l-1}
sumr−sumi=sumi−1−suml−1 的
i
i
i ,将式子变化为:
s
u
m
r
+
s
u
m
l
−
1
=
s
u
m
i
+
s
u
m
i
−
1
sum_r+sum_{l-1}=sum_i+sum_{i-1}
sumr+suml−1=sumi+sumi−1
因此可以将每个 i i i 放入 s u m i + s u m i − 1 sum_i+sum_{i-1} sumi+sumi−1 的桶中统计,由于必定有解,因此查找时直接在对应桶中二分查找 [ l , r ] [l,r] [l,r] 区间内的 i i i 即可。
注意 s u m i + s u m i − 1 sum_i+sum_{i-1} sumi+sumi−1 可能为负,因此需要加偏移量 B = 2 n B=2n B=2n 。
参考代码
#include<bits/stdc++.h>
using namespace std;
const int MAXN=300300;
const int B=MAXN<<1;
char s[MAXN];
int sum[MAXN];
vector<int> vec[MAXN<<2];
int main()
{
int T,n,q,i,l,r,dif,x,id;
scanf("%d",&T);
while(T--)
{
scanf("%d%d",&n,&q);
scanf("%s",s+1);
for(i=B-2*n;i<=B+2*n;i++)
vec[i].clear();
for(i=1;i<=n;i++)
{
if(i&1)
sum[i]=sum[i-1]+(s[i]=='+'?1:-1);
else
sum[i]=sum[i-1]-(s[i]=='+'?1:-1);
vec[sum[i]+sum[i-1]+B].push_back(i);
}
while(q--)
{
scanf("%d%d",&l,&r);
dif=sum[r]-sum[l-1];
if(dif==0)
printf("0\n");
else
{
if(dif%2)
printf("1\n");
else
{
printf("2\n");
printf("%d ",l);
l++;
}
x=sum[r]+sum[l-1]+B;
id=lower_bound(vec[x].begin(),vec[x].end(),l)-vec[x].begin();
printf("%d\n",vec[x][id]);
}
}
}
}
E. Rescue Niwen!
题目大意
定义长度为 n n n 的字符串 s s s 的扩展序列为 s 1 , s 1 s 2 , . . . , s 1 s 2 . . . s n , s 2 , s 2 s 3 , . . . , s 2 s 3 . . . s n , s 3 , s 3 s 4 , . . . s n − 1 s n , s n s_1,s_1s_2,...,s_1s_2...s_n,s_2,s_2s_3,...,s_2s_3...s_n,s_3,s_3s_4,...s_{n-1}s_n,s_n s1,s1s2,...,s1s2...sn,s2,s2s3,...,s2s3...sn,s3,s3s4,...sn−1sn,sn 。例如字符串 ‘abcd’ 的扩展序列为 ‘a’,‘ab’,‘abc’,‘abcd’,‘b’,‘bc’,‘bcd’,‘c’,‘cd’,‘d’ 。
现在给定长度为 n ( 1 ≤ n ≤ 5000 ) n(1 \leq n \leq 5000) n(1≤n≤5000) 的字符串 s s s ,求其扩展序列的最长递增子序列长度。
题解
如果要快速比较扩展序列中两个字符串的大小,需要知道它们的最长公共前缀。
设
c
i
,
j
c_{i,j}
ci,j 表示从
s
i
s_i
si 与
s
j
s_j
sj 开始的两个后缀的最长公共前缀长度,易得
c
i
,
j
=
{
c
i
+
1
,
j
+
1
s
i
=
s
j
0
s
i
≠
s
j
c_{i,j}=\begin{cases} c_{i+1,j+1} &s_i=s_j \\ 0 &s_i \neq s_j \end{cases}
ci,j={ci+1,j+10si=sjsi=sj
设扩展序列中第
i
i
i 个字符串为
A
i
A_i
Ai 。
若
i
<
j
,
A
i
<
A
j
i<j,A_i<A_j
i<j,Ai<Aj 且
A
i
A_i
Ai 与
A
j
A_j
Aj 的首字符在
s
s
s 中的来源并不相同,则包含
A
i
,
A
j
A_i,A_j
Ai,Aj 的最长递增子序列,必定也包含其他首字符与
A
i
A_i
Ai 的相同,且长度比
A
i
A_i
Ai 长的其他字符串。
例如
s
=
s=
s=‘abcd’ ,若最长递增子序列包含 ‘b’ 与 ‘c’ ,则必定也包含 ‘bc’,‘bcd’ 。
设
d
p
i
dp_i
dpi 表示扩展序列中以首字符为
s
i
s_i
si 的字符串结尾的最长递增子序列长度,则有
d
p
i
=
max
j
=
1
i
−
1
{
d
p
j
−
c
i
,
j
∣
s
j
+
c
i
,
j
<
s
i
+
c
i
,
j
}
+
n
−
i
+
1
dp_i=\max_{j=1}^{i-1}\{dp_j-c_{i,j}|s_{j+c_{i,j}}<s_{i+c_{i,j}}\}+n-i+1
dpi=j=1maxi−1{dpj−ci,j∣sj+ci,j<si+ci,j}+n−i+1
参考代码
#include<bits/stdc++.h>
using namespace std;
const int MAXN=5050;
char s[MAXN];
int c[MAXN][MAXN],dp[MAXN];
int main()
{
int T,n,ans,i,j;
scanf("%d",&T);
while(T--)
{
scanf("%d%s",&n,s+1);
for(i=n;i>=1;i--)
{
for(j=n;j>=1;j--)
{
if(s[i]==s[j])
c[i][j]=c[i+1][j+1]+1;
else
c[i][j]=0;
}
}
ans=0;
for(i=1;i<=n;i++)
{
dp[i]=n-i+1;
for(j=1;j<=i-1;j++)
{
if(dp[j]-c[i][j]+n-i+1>dp[i]&&s[j+c[i][j]]<s[i+c[i][j]])
dp[i]=dp[j]-c[i][j]+n-i+1;
}
if(dp[i]>ans)
ans=dp[i];
}
printf("%d\n",ans);
}
}
F. Tubular Bells
题目大意
有一个长度为 n ( 3 ≤ n ≤ 1 0 5 ) n~(3 \leq n \leq 10^5) n (3≤n≤105) 的包含 [ l , r ] ( 1 ≤ l ≤ r ≤ 2 ⋅ 1 0 5 , r − l + 1 = n ) [l,r]~(1 \leq l \leq r \leq 2 \cdot 10^5,r-l+1=n) [l,r] (1≤l≤r≤2⋅105,r−l+1=n) 范围内所有数的序列 a a a ,你只知道 n n n ,但并不知道 l , r l,r l,r 。你需要通过交互问答猜测这个序列。
最多询问 n + 5000 n+5000 n+5000 次,每次询问输出两个正整数 x , y ( 1 ≤ x ≤ y ≤ n , x ≠ y ) x,y~(1 \leq x \leq y\leq n,x\neq y) x,y (1≤x≤y≤n,x=y) ,系统会返回 l c m ( a x , a y ) lcm(a_x,a_y) lcm(ax,ay) 。
题解
n
(
n
−
1
)
2
≤
5000
\frac{n(n-1)}{2}\leq 5000
2n(n−1)≤5000 ,即
n
≤
100
n\leq 100
n≤100 时,可以直接枚举所有
(
i
,
j
)
(i,j)
(i,j) 进行询问,设
c
i
,
j
c_{i,j}
ci,j 表示询问
(
i
,
j
)
(i,j)
(i,j) 对的结果。
由于
g
c
d
(
x
,
x
+
1
)
=
1
gcd(x,x+1)=1
gcd(x,x+1)=1 ,因此
g
c
d
(
x
y
,
(
x
+
1
)
y
)
=
1
gcd(xy,(x+1)y)=1
gcd(xy,(x+1)y)=1 。
易得
a
n
s
i
=
g
c
d
j
≠
i
{
c
i
,
j
}
ans_i=gcd_{j\neq i}\{c_{i,j}\}
ansi=gcdj=i{ci,j}
特别注意的是,当 n = 3 n=3 n=3 时,对于中间值 y = l + 1 y=l+1 y=l+1 来说,不存在 x x x 和 x + 1 x+1 x+1 使得 g c d ( x y , ( x + 1 ) y ) = 1 gcd(xy,(x+1)y)=1 gcd(xy,(x+1)y)=1 。
- 当 y = l + 1 y=l+1 y=l+1 为偶数,即 l l l 为奇数时, g c d ( l ( l + 1 ) , ( l + 2 ) ( l + 1 ) ) = l + 1 gcd(l(l+1),(l+2)(l+1))=l+1 gcd(l(l+1),(l+2)(l+1))=l+1 ,不影响结果。
- 当 y = l + 1 y=l+1 y=l+1 为奇数,即 l l l 为偶数时, g c d ( l ( l + 1 ) , ( l + 2 ) ( l + 1 ) ) = 2 ( l + 1 ) gcd(l(l+1),(l+2)(l+1))=2(l+1) gcd(l(l+1),(l+2)(l+1))=2(l+1) ,需要将其(最大值)除 2 2 2 才能得到正确答案。
n ( n − 1 ) 2 > 5000 \frac{n(n-1)}{2}> 5000 2n(n−1)>5000 ,即 n > 100 n >100 n>100 时,若能找到在 ( l + r 2 , r ] (\frac{l+r}{2},r] (2l+r,r] 区间内的素数 p p p ,则易得 p p p 和 [ l , r ] [l,r] [l,r] 范围内的其他数互质。
考虑随机化算法。
当范围越大时,素数出现概率越小,考虑最小的情况,即
l
=
1
0
5
+
1
,
r
=
2
⋅
1
0
5
l=10^5+1,r=2\cdot 10^5
l=105+1,r=2⋅105 ,其中
[
150000
,
200000
]
[150000,200000]
[150000,200000] 范围内共有
4136
4136
4136 个素数,占比
4.136
%
4.136\%
4.136% 。
因此可以任选
m
=
250
m=250
m=250 个数,找出其中最大的素数,基本可以视为是
(
l
+
r
2
,
r
]
(\frac{l+r}{2},r]
(2l+r,r] 区间内的素数。
对于任意一个数
y
y
y 来说,若能找到一对互质的数
x
1
,
x
2
x_1,x_2
x1,x2 则
g
c
d
(
l
c
m
(
x
1
,
y
)
,
l
c
m
(
x
2
,
y
)
)
=
y
gcd(lcm(x_1,y),lcm(x_2,y))=y
gcd(lcm(x1,y),lcm(x2,y))=y
根据黎曼猜想推导可得,任意两个自然数互质的概率是
6
π
2
≈
60
%
\frac{6}{\pi^2}\approx 60\%
π26≈60% ,是一个比较大的概率。
因此对于下标
u
u
u ,可以任选
k
=
20
k=20
k=20 个
v
i
(
u
≠
v
i
)
v_i ~(u \neq v_i)
vi (u=vi) ,他们询问结果的
g
c
d
gcd
gcd 基本上就可以视为
a
n
s
u
ans_u
ansu ,再查素数表即可知道其是否是素数。
由于是随机算法,具体 m m m 和 k k k 的值需要调参,保证 m k ≤ 5000 mk \leq 5000 mk≤5000 即可。
参考代码
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int MAXN=100100;
const int MAXM=200200;
int npri[MAXM];
ll ans[MAXN];
ll rd()
{
return 1ll*rand()*RAND_MAX+rand();
}
ll gcd(ll x,ll y)
{
return x%y?gcd(y,x%y):y;
}
ll lcm(ll x,ll y)
{
return x*y/gcd(x,y);
}
ll query(int x,int y)
{
cout<<"? "<<x<<" "<<y<<endl;
ll ret;
cin>>ret;
return ret;
}
int main()
{
int T,n,i,j,mx,id,mxid,m,u,v;
ll g,c[110][110];
ios::sync_with_stdio(false);
cin.tie(0);
for(i=2;i<MAXM;i++)
{
if(npri[i])
continue;
for(j=i+i;j<MAXM;j+=i)
npri[j]=1;
}
cin>>T;
while(T--)
{
cin>>n;
if(n<=100)
{
for(i=1;i<=n;i++)
for(j=i+1;j<=n;j++)
c[i][j]=c[j][i]=query(i,j);
for(i=1;i<=n;i++)
{
ans[i]=c[i][i%n+1];
for(j=1;j<=n;j++)
{
if(i==j)
continue;
ans[i]=gcd(ans[i],c[i][j]);
}
}
if(n==3&&ans[1]%2==0&&ans[2]%2==0&&ans[3]%2==0)
{
mxid=1;
for(i=1;i<=n;i++)
{
if(ans[mxid]<ans[i])
mxid=i;
}
ans[mxid]/=2;
}
}
else
{
m=250;
mx=id=-1;
while(m--)
{
u=rd()%n+1;
g=0;
for(i=0;i<20;i++)
{
v=rd()%n+1;
while(u==v)
v=rd()%n+1;
if(g==0)
g=query(u,v);
else
g=gcd(g,query(u,v));
}
if(!npri[g])
{
if(g>mx)
{
mx=g;
id=u;
}
}
}
for(i=1;i<=n;i++)
{
if(i==id)
ans[i]=mx;
else
ans[i]=query(id,i)/mx;
}
}
cout<<"!";
for(i=1;i<=n;i++)
cout<<" "<<ans[i];
cout<<endl;
}
}