Codeforces Round #741 (Div. 2) 简单记录
这场也打了,再简单记录一下
A
给一个区间\([l, r]\),求\((a, b)\)满足\(1 \leq a \leq b \leq r\),使\(b \mod a\)最大。
观察样例\((l = 8, r = 26)\),容易发现不考虑\(l\)的限制的情况,对于一个\(r\)答案最大的情况是取到\(2n + 1 \leq r\)的最大的\(n\),在这种情况下不妨取\(b = r\)可使\(l\)的限制最松。考虑到\(r\)的奇偶性会使上面的\(a\)的取值发生变化,不妨先取\(n = \lfloor \frac{r}{2} \rfloor, b = r\),若\(l \leq n + 1\),取\(a = n + 1\)可使答案取到最大值,否则\(a = l\)。
int T;
read(T);
for (int l, r; T--; ) {
read(l), read(r);
if (l == r) puts("0");
else {
int n = r / 2;
int b = r, a = max(l, n + 1);
printf("%d\n", b % a);
}
}
B
给出一个\(k\)位的不含\(0\)的十进制数,想要删掉尽可能多的位数,使得剩下的数不是一个素数。
若这个数中含有\(1, 4, 6, 8, 9\),直接删到这一位即可;否则这些数中只有\(2, 3, 5, 7\),此时一定满足\(k \geq 2\),寻找这些数位组成的合法两位数即可。
scanf("%d", &T);
for (; T--; ) {
scanf("%d", &n);
scanf("%s", s + 1);
for (int i = 1; i <= n; i++) a[i] = s[i] - '0';
bool ok = 0;
for (int i = 1; i <= n; i++) {
if (a[i] == 1 || a[i] == 4 || a[i] == 6 || a[i] == 8 || a[i] == 9) {
ok = 1;
printf("1\n%d\n", a[i]);
break;
}
}
if (ok) continue;
for (int i = 1; i <= n; i++) {
if (a[i] == 2) {
for (int j = i + 1; j <= n; j++)
if (a[j] == 2 || a[j] == 5 || a[j] == 7) {
ok = 1;
printf("2\n%d%d\n", a[i], a[j]);
break;
}
if (ok) break;
} else if (a[i] == 3) {
for (int j = i + 1; j <= n; j++)
if (a[j] == 2 || a[j] == 5 || a[j] == 3) {
ok = 1;
printf("2\n%d%d\n", a[i], a[j]);
break;
}
if (ok) break;
} else if (a[i] == 5) {
for (int j = i + 1; j <= n; j++)
if (a[j] == 2 || a[j] == 5 || a[j] == 7) {
ok = 1;
printf("2\n%d%d\n", a[i], a[j]);
break;
}
if (ok) break;
} else if (a[i] == 7) {
for (int j = i + 1; j <= n; j++)
if (a[j] == 2 || a[j] == 5 || a[j] == 7) {
ok = 1;
printf("2\n%d%d\n", a[i], a[j]);
break;
}
if (ok) break;
}
}
if (ok) continue;
// assert(0);
}
C
给出一个长度为\(n\)的\(01\)串,找两个区间长度\(\geq \lfloor \frac{n}{2} \rfloor\)的不完全相同的区间\([l_1, r_1]\)和\([l_2, r_2]\)满足\(f(l_1 : r_1)\)是\(f(l_2, r_2)\)的若干倍,倍数不可以是\(0\),其中\(f(l : r)\)表示从\(l\)位到\(r\)位的\(01\)构成的二进制数的大小,可以有前导\(0\)。
第一想法是在靠近结尾的位置找个\(0\),假设\(0\)的位置是\(p\),那么\([1, p], [1, p - 1]\)就是一个合法的答案;然后发现\(0\)放在开头也没有影响,如果\(p \leq \frac{(n + 1)}{2}\),那么\([p, n], [p + 1, n]\)也是合法的答案;最后就是找不到\(0\)的全\(1\)串,只要输出\([1, mid], [mid + 1, n]\)就可以了。
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
typedef double db;
typedef pair <int, int> pin;
#ifndef ONLINE_JUDGE
bool MEMORY_ST;
#endif
const int N = 2e4 + 5;
const ll P = 998244353LL;
int T, n;
char s[N];
inline int get() {
for (int i = 1; i <= n; i++)
if (s[i] == '0')
return i;
return 0;
}
#ifndef ONLINE_JUDGE
bool MEMORY_ED;
#endif
int main() {
#ifndef ONLINE_JUDGE
freopen("sample.in", "r", stdin);
clock_t st_clock = clock();
#endif
scanf("%d", &T);
for (; T--; ) {
scanf("%d", &n);
scanf("%s", s + 1);
int p = get();
if (p == 0) {
int mid;
if (n & 1) {
mid = (n + 1) / 2;
printf("%d %d %d %d\n", 1, mid - 1, mid + 1, n);
} else {
mid = n / 2;
printf("%d %d %d %d\n", 1, mid, mid + 1, n);
}
} else {
int mid;
if (n & 1) {
mid = (n + 1) / 2;
if (p <= mid) printf("%d %d %d %d\n", p, n, p + 1, n);
else printf("%d %d %d %d\n", 1, p, 1, p - 1);
} else {
mid = n / 2;
if (p <= mid) printf("%d %d %d %d\n", p, n, p + 1, n);
else printf("%d %d %d %d\n", 1, p, 1, p - 1);
}
}
}
#ifndef ONLINE_JUDGE
clock_t ed_clock = clock();
printf("time = %f ms\n", (double)(ed_clock - st_clock) / CLOCKS_PER_SEC * 1000);
printf("memory = %.2f MB\n", (&MEMORY_ED - &MEMORY_ST) / 1024.0 / 1024.0);
#endif
return 0;
}
D
话说最近的CF是真的喜欢出D1、D2这样的题。
给出一个\(+\)和\(-\)构成的序列,\(+\)代表\(1\),\(-\)代表\(-1\),定义一个序列\(a\)的价值为\(\sum_{i = 1}^{n}(-1)^{i - 1}a_i\),现在可以从序列中选择若干个数删掉(删掉改变后面的数的奇偶性),想要使序列的价值为\(0\)。
给出长度为\(n\)的一长串序列和\(q\)个询问,每个询问为一个区间\((l, r)\),问最少删掉几个数,使\([l, r]\)的价值为\(0\)。
分割线
结论:答案只可能为\(0, 1, 2\)。
首先把偶数位置上的数都负过来得到\(a_i\),做个前缀和。如果\(sum(l, r) = 0\),显然答案为\(0\)。如果区间长度是个偶数,那么删\(1\)个肯定不能使答案为\(0\),接下来证一下对于任何区间长度为奇数的区间都一定能找到删\(1\)个的方法:
假设区间是\((l, r)\)(\((r - l + 1)\)是个奇数),如果存在一个删的位置\(p\)满足条件,存在两种情况:
-
\(p = l\),此时\(sum(l + 1 : r) = 0\)。
-
\(p \in (l, r]\),此时\(p\)满足\(sum(l : p - 1) - sum(p + 1 : r) = 0\),写成前缀和的形式:\(sum(p - 1) - sum(l - 1) - sum(r) + sum(p) = 0\),移项,\(sum(p) + sum(p - 1) = sum(l - 1) + sum(r)\)
由于\(a\)的变化幅度很有限,\(sum\)可以近似看作一个连续函数,如果设\(b_i = sum_i + sum_{i - 1}\)那么\(b\)的变化幅度也很有限,近似地用一下零点存在定理之类的东西就能看出来了。
这样的话我们把所有的偶数长度的区间都暴力删去一个\(r\),然后转化成奇数长度的区间,再删去一个;关于奇数长度的区间怎么找,可以暴力统计每个\(sum_i + sum_{i - 1}\)出现的位置,找到一个最接近\(l\)和\(r\)的就可以了。
一开始我天真地写了个2log被卡成猪头了。
D2代码。
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
typedef double db;
typedef pair <int, int> pin;
#ifndef ONLINE_JUDGE
bool MEMORY_ST;
#endif
const int N = 6e5 + 5;
const ll P = 998244353LL;
int T, n, qn, a[N], b[N], sum[N], ans;
char str[N];
bool flag;
struct AnsNode {
int siz, res1, res2;
};
vector <AnsNode> q;
struct Query {
int l, v, id;
};
vector <Query> vec[N];
namespace Fwrite {
const int L = 1 << 15;
char buf[L], *pp = buf;
void Putchar(const char c) {
if(pp - buf == L) fwrite(buf, 1, L, stdout), pp = buf;
*pp++ = c;
}
template<typename T>
void print(T x) {
if(x < 0) {
Putchar('-');
x = -x;
}
if(x > 9) print(x / 10);
Putchar(x % 10 + '0');
}
void fsh() {
fwrite(buf, 1, pp - buf, stdout);
pp = buf;
}
template <typename T>
inline void write(T x, char ch = 0) {
print(x);
if (ch != 0) Putchar(ch);
// fsh();
}
} using Fwrite::write;
inline bool push(int l, int r, int id) {
assert(r - l + 1 >= 2);
assert((r - l + 1) & 1);
if (sum[r] - sum[l] == 0) return 0;
vec[r].emplace_back((Query) {l + 1, sum[r] + sum[l - 1], id});
return 1;
}
inline void solve() {
set <pin> s;
for (int i = 2; i <= n; i++) {
set <pin>::iterator it = s.lower_bound(pin(sum[i] + sum[i - 1], 0));
if (it != s.end() && it->first == sum[i] + sum[i - 1]) s.erase(it);
s.insert(pin(sum[i] + sum[i - 1], i));
for (int j = 0; j < vec[i].size(); j++) {
it = s.lower_bound(pin(vec[i][j].v, 0));
q[vec[i][j].id].res1 = it->second;
}
}
}
#ifndef ONLINE_JUDGE
bool MEMORY_ED;
#endif
int main() {
#ifndef ONLINE_JUDGE
freopen("sample.in", "r", stdin);
freopen("test.out", "w", stdout);
clock_t st_clock = clock();
#endif
scanf("%d", &T);
for (; T--; ) {
scanf("%d%d", &n, &qn);
scanf("%s", str + 1);
for (int i = 1; i <= n; i++) {
if (str[i] == '+') a[i] = 1;
else a[i] = -1;
if (!(i & 1)) a[i] = -a[i];
}
for (int i = 1; i <= n; i++) sum[i] = sum[i - 1] + a[i];
// prework();
for (int i = 1; i <= n; i++) vec[i].clear();
q.clear();
for (int l, r; qn--; ) {
scanf("%d%d", &l, &r);
if (l == r) {
q.emplace_back((AnsNode) {1, l, 0});
} else {
if (sum[r] - sum[l - 1] == 0) q.emplace_back((AnsNode) {0, 0, 0});
else if ((r - l + 1) & 1) {
q.emplace_back((AnsNode) {1, 0, 0});
if (!push(l, r, q.size() - 1))
q[q.size() - 1].res1 = l;
} else {
if (l + 1 == r) {
q.emplace_back((AnsNode) {2, l, r});
} else {
q.emplace_back((AnsNode) {2, 0, r});
if (!push(l, r - 1, q.size() - 1))
q[q.size() - 1].res1 = l;
}
}
}
}
solve();
for (int i = 0; i < q.size(); i++) {
write(q[i].siz, '\n');
if (q[i].siz == 1) write(q[i].res1, '\n');
else if (q[i].siz == 2) write(q[i].res1, ' '), write(q[i].res2, '\n');
}
}
Fwrite::fsh();
#ifndef ONLINE_JUDGE
clock_t ed_clock = clock();
printf("time = %f ms\n", (double)(ed_clock - st_clock) / CLOCKS_PER_SEC * 1000);
printf("memory = %.2f MB\n", (&MEMORY_ED - &MEMORY_ST) / 1024.0 / 1024.0);
#endif
return 0;
}
E
给一个字符串\(s\),定义其“扩展”是将其每一个后缀的前缀按长度从小到大排列之后按顺序连接起来的序列,求其最长上升子序列。
后缀数组可以实现\(O(1)\)判断两个子串的大小关系,于是\(O(n^2\log n)\)的暴力就可过了。
请使用\(\text{lower_bound}\)代替手动的二分。
卡着时限过的代码:
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
typedef double db;
typedef pair <int, int> pin;
#ifndef ONLINE_JUDGE
bool MEMORY_ST;
#endif
const int N = 5005;
const ll P = 998244353LL;
const int inf = 0x3f3f3f3f;
int T, n, lcp[N][N];
int sa[N], rk[N], oldrk[N << 1], id[N], px[N], cnt[N], ht[N];
char str[N];
inline bool cmp(int x, int y, int w) {
return oldrk[x] == oldrk[y] && oldrk[x + w] == oldrk[y + w];
}
inline void saInit() {
memset(cnt, 0, sizeof(cnt));
int m = 200, p;
for (int i = 1; i <= n; i++) ++cnt[rk[i] = str[i]];
for (int i = 1; i <= m; i++) cnt[i] += cnt[i - 1];
for (int i = n; i >= 1; i--) sa[cnt[rk[i]]--] = i;
for (int w = 1; w < n; w <<= 1, m = p) {
p = 0;
for (int i = n; i > n - w; i--) id[++p] = i;
for (int i = 1; i <= n; i++)
if (sa[i] > w) id[++p] = sa[i] - w;
memset(cnt, 0, sizeof(cnt));
for (int i = 1; i <= n; i++) ++cnt[px[i] = rk[id[i]]];
for (int i = 1; i <= m; i++) cnt[i] += cnt[i - 1];
for (int i = n; i >= 1; i--) sa[cnt[px[i]]--] = id[i];
memcpy(oldrk, rk, sizeof(rk));
p = 0;
for (int i = 1; i <= n; i++)
rk[sa[i]] = cmp(sa[i], sa[i - 1], w) ? p : ++p;
}
for (int i = 1; i <= n; i++) rk[sa[i]] = i;
for (int i = 1, k = 0; i <= n; i++) {
if (k) --k;
for (; str[i + k] == str[sa[rk[i] - 1] + k]; ++k);
ht[rk[i]] = k;
}
}
inline void lcpInit() {
/* lcp[i][j] : lcp(sa[i], sa[j]) */
for (int j = 1; j <= n; j++) {
lcp[j][j] = n - sa[j] + 1;
for (int i = j - 1; i >= 1; i--) {
lcp[i][j] = min(lcp[i + 1][j], ht[i + 1]);
lcp[j][i] = lcp[i][j];
}
}
}
inline bool seqCmp(pin u, pin v) {
int a = u.first, b = u.second, c = v.first, d = v.second;
/* compare str[a : b] and str[c : d] */
/* 1 : str[a : b] < str[c : d] */
int t = lcp[rk[a]][rk[c]];
if (t >= b - a + 1 || t >= d - c + 1) return b - a < d - c;
else return rk[a] < rk[c];
}
#ifndef ONLINE_JUDGE
bool MEMORY_ED;
#endif
int main() {
#ifndef ONLINE_JUDGE
freopen("sample.in", "r", stdin);
clock_t st_clock = clock();
#endif
scanf("%d", &T);
for (; T--; ) {
scanf("%d", &n);
scanf("%s", str + 1);
saInit();
lcpInit();
vector <pin> f;
for (int i = 1; i <= n; i++) {
for (int j = i; j <= n; j++) {
pin cur = pin(i, j);
if (!f.empty()) {
if (!seqCmp(cur, f.back())) f.emplace_back(pin(i, j));
else {
int p = lower_bound(f.begin(), f.end(), cur, seqCmp) - f.begin();
f[p] = pin(i, j);
}
} else f.emplace_back(pin(i, j));
}
}
printf("%d\n", f.size());
}
#ifndef ONLINE_JUDGE
clock_t ed_clock = clock();
printf("time = %f ms\n", (double)(ed_clock - st_clock) / CLOCKS_PER_SEC * 1000);
printf("memory = %.2f MB\n", (&MEMORY_ED - &MEMORY_ST) / 1024.0 / 1024.0);
#endif
return 0;
}
正解是这样的:
观察到如果一个子串\([l, r]\)被选了,那么\([l, n]\)一定会被选。假设下一个是\([l', r']\)(\(l' > l\)),如果子串\([l', r']\)因为某个字母的存在使其字典序严格大于后缀\(l\),那么\(r \neq n\)时一定不优;否则后缀\(l'\)是后缀\(l\)的前缀,此时就可以不要后缀\(l'\)所有的答案,答案不会变差。
于是设\(f(i)\)表示\(i\)结尾的LIS的长度,转移的时候默认\(f(i)\)的最后一位是子串\([i, n]\)。用后缀\(j\)转移后缀\(i\)时所选取的位置一定大于\(lcp(i, j)\),否则没有贡献。
F
交互题。
给出一个长度为\(n\)的序列\(a[1 : n]\),已知序列中的数是\([l : r]\)的一个排列。每次可以询问两个数\((x, y)\),得到\(\text{lcm}(a_x, a_y)\),使用不超过\(n + 5000\)组询问看出\(a\)是什么。
观察这个询问次数的限制,一般来说这种都是用\(5000\)次询问弄出一个\(a_i\),然后询问每个\((j, i)\)解出\(a_j\)。
什么样的数可以解出\(a_j\)呢?因为我们已知的信息是\(\text{lcm}(a_i, a_j)\)的形式,我们希望能找到一个\(a_i\)满足\(\forall j \neq i, \gcd(a_i, a_j) = 1\)。这个序列中最大的素数\(p\)一定可以;其实找一个\(> \frac{r}{2}\)的素数就也可以了。
这里有一个问题,是不是所有\((l, r)\)中一定存在一个\(> \frac{r}{2}\)的素数?并不会严格证明,感觉就是……
接下来的问题是如何判定这样的一个素数\(p\),可以随机摇三个互不相等的数\(x, y, z\),询问\((x, y)\)和\((x, z)\),如果我们认定\(a_x\)是一个满足条件的数,那么\(a_x\)一定是这两个询问的答案中的因子。这样的随机可以进行\(2500\)次,每一次我们都暴力分解这个两个\(\text{lcm}\),如果其最大质因子一样就用\(x\)更新答案,保留找到的最大的那个质因子就行。
那么合法的满足条件的三元组\((x, y, z)\)大约有\(\frac{n}{2\log n}(n - 1)(n - 2)\)个,总共有\((n(n - 1)(n - 2))\)个,摇一次摇不到的概率大约是\(\frac{39}{40}\),摇\(2500\)次一个都摇不到的概率是:
不得不说居然还有点余裕……
剩下还有一个小问题就是\(n\)很小的时候,这个区间中可能一个素数也没有。打个表发现这样的\(n\)最大可能为\(85\),那么我们考虑暴力询问出两两的\(\text{lcm}\),容易看到
上面这个东西基本上都成立,只要\(n\)稍微大点有个偶数有个奇数就都成立了,唯一的反例是\([2, 3, 4]\)这种情况,中间\(3\)那个位置会是一个\(6\),特判掉它。
话说本地测交互题真的很麻烦……
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
typedef double db;
typedef pair <int, int> pin;
#ifndef ONLINE_JUDGE
bool MEMORY_ST;
#endif
const int N = 2e5 + 5;
const int Maxn = 2e5;
const ll P = 998244353LL;
int n, cnt, pcnt, pri[N];
ll a[N];
bool vis[N];
inline void sieve() {
// pri[++pcnt] = 1;
for (int i = 2; i <= Maxn; i++) {
if (!vis[i]) {
pri[++pcnt] = i;
}
for (int j = 1; j <= pcnt && i * pri[j] <= Maxn; j++) {
vis[i * pri[j]] = 1;
if (i % pri[j] == 0) break;
}
}
}
namespace LocalTest {
const int M = 1e5 + 5;
int qcnt = 0;
ll b[M];
inline void readin() {
qcnt = 0;
for (int i = 1; i <= n; i++) cin >> b[i];
}
inline ll lcm(ll x, ll y) {
return x / __gcd(x, y) * y;
}
inline ll query(int x, int y) {
if (x == y) {
cout << "In query " << x << " and " << y << " x == y" << endl;
return 0;
}
++qcnt;
if (qcnt > n + 5000) {
cout << "too many queries" << endl;
}
return lcm(b[x], b[y]);
}
inline void chkAns() {
bool ok = 1;
for (int i = 1; i <= n; i++)
if (a[i] != b[i]) {
ok = 0;
cout << "wa" << endl;
break;
}
if (ok) cout << "ok" << endl;
}
}
inline ll query(int x, int y) {
++cnt;
#ifndef ONLINE_JUDGE
return LocalTest::query(x, y);
#endif
cout << "? " << x << " " << y << endl;
ll res;
cin >> res;
return res;
}
namespace PollarRho {
int t;
long long max_factor;
long long quick_pow(long long x, long long p, long long mod) { //快速幂
long long ans = 1;
while (p) {
if (p & 1) ans = (__int128_t)ans * x % mod;
x = (__int128_t)x * x % mod;
p >>= 1;
}
return ans;
}
bool Miller_Rabin(long long p) { //判断素数
if (p < 2) return 0;
if (p == 2) return 1;
if (p == 3) return 1;
// if (p <= Maxn) return !vis[p];
long long d = p - 1, r = 0;
while (!(d & 1)) ++r, d >>= 1; //将d处理为奇数
for (long long k = 0; k < 10; ++k) {
long long a = rand() % (p - 2) + 2;
long long x = quick_pow(a, d, p);
if (x == 1 || x == p - 1) continue;
for (int i = 0; i < r - 1; ++i) {
x = (__int128_t)x * x % p;
if (x == p - 1) break;
}
if (x != p - 1) return 0;
}
return 1;
}
long long Pollard_Rho(long long x) {
long long s = 0, t = 0;
long long c = (long long)rand() % (x - 1) + 1;
int step = 0, goal = 1;
long long val = 1;
for (goal = 1;; goal *= 2, s = t, val = 1) { //倍增优化
for (step = 1; step <= goal; ++step) {
t = ((__int128_t)t * t + c) % x;
val = (__int128_t)val * abs(t - s) % x;
if ((step % 127) == 0) {
long long d = __gcd(val, x);
if (d > 1) return d;
}
}
long long d = __gcd(val, x);
if (d > 1) return d;
}
}
void fac(long long x) {
if (x <= max_factor || x < 2) return;
if (Miller_Rabin(x)) { //如果x为质数
max_factor = max(max_factor, x); //更新答案
return;
}
long long p = x;
while (p >= x) p = Pollard_Rho(x); //使用该算法
while ((x % p) == 0) x /= p;
fac(x), fac(p); //继续向下分解x和p
}
// int main() {
// scanf("%d", &t);
// while (t--) {
// srand((unsigned)time(NULL));
// max_factor = 0;
// scanf("%lld", &n);
// fac(n);
// if (max_factor == n) //最大的质因数即自己
// printf("Prime\n");
// else
// printf("%lld\n", max_factor);
// }
// return 0;
// }
inline bool chk(ll cur) {
srand((unsigned)time(NULL));
max_factor = 0;
ll t = cur;
fac(t);
// cout << "1" << endl;
ll x = max_factor, y = t / max_factor;
// cout << t << " " << x << " " << y << endl;
// cout << vis[x] << " " << vis[y] << endl;
if (x <= Maxn && y <= Maxn && !vis[x] && !vis[y]) return 1;
else return 0;
}
inline ll get(ll cur) {
srand((unsigned)time(NULL));
max_factor = 0;
ll t = cur;
fac(t);
return max_factor;
}
}
namespace Sub1 {
const int M = 105;
ll res[M][M];
inline void solve() {
cnt = 0;
for (int i = 1; i <= n; i++)
for (int j = i + 1; j <= n; j++) {
res[i][j] = query(i, j);
res[j][i] = res[i][j];
}
assert(cnt <= n + 5000);
for (int i = 1; i <= n; i++) {
a[i] = 0;
for (int j = 1; j <= n; j++) {
if (i == j) continue;
a[i] = __gcd(a[i], res[i][j]);
}
}
vector <int> tmp;
for (int i = 1; i <= n; i++) tmp.emplace_back(a[i]);
sort(tmp.begin(), tmp.end());
if (n == 3 && tmp[0] + tmp[1] == tmp[2] && !(tmp[0] & 1) && !(tmp[1] & 1)) {
for (int i = 1; i <= n; i++)
if (a[i] == tmp[2])
a[i] /= 2;
tmp[2] /= 2;
sort(tmp.begin(), tmp.end());
}
for (int i = 1; i < n; i++) assert(tmp[i] - tmp[0] == i);
}
}
namespace Sub2 {
const int M = 1e5 + 5;
const int L = 4e6 + 5;
const int MaxL = 4e6;
ll res[M];
bool flag[L];
inline void prework() {
for (int i = 1; i <= pcnt; i++)
for (int j = 1; j <= pcnt && pri[j] * pri[i] <= MaxL; j++) {
flag[pri[i] * pri[j]] = 1;
}
}
inline bool chk(ll cur) {
if (cur <= MaxL) return flag[cur];
for (int i = pcnt; i >= 1; i--) {
if (cur % pri[i] == 0) {
ll t = cur / pri[i];
if (t <= Maxn) {
if (!vis[t]) return 1;
} else break;
}
}
return 0;
}
inline void solve() {
cnt = 0;
int p = 0;
ll mx = 0;
mt19937 myrand(time(0));
for (int i = 1; i <= 2500; ) {
int x = myrand() % n + 1, y = myrand() % n + 1, z = myrand() % n + 1;
if (x == y || x == z || y == z) continue;
i++;
ll cur1 = query(x, y), cur2 = query(x, z);
ll res1 = PollarRho::get(cur1), res2 = PollarRho::get(cur2);
if (res1 != res2) continue;
if (res1 > mx) {
p = x;
mx = res1;
}
}
assert(p != 0);
for (int i = 1; i <= n; i++) {
if (i == p) continue;
res[i] = query(i, p);
}
assert(cnt <= n + 5000);
a[p] = 0;
for (int i = 1; i <= n; i++) {
if (i == p) continue;
a[p] = __gcd(a[p], res[i]);
}
assert(!vis[a[p]]);
if (a[p] != 0) {
for (int i = 1; i <= n; i++) {
if (i == p) continue;
a[i] = res[i] / a[p];
}
} else assert(0);
}
}
#ifndef ONLINE_JUDGE
bool MEMORY_ED;
#endif
int main() {
#ifndef ONLINE_JUDGE
freopen("sample.in", "r", stdin);
freopen("sample.out", "w", stdout);
clock_t st_clock = clock();
#endif
sieve();
Sub2::prework();
int T;
cin >> T;
for (; T--; ) {
cin >> n;
#ifndef ONLINE_JUDGE
LocalTest::readin();
#endif
if (n <= 100) {
Sub1::solve();
} else {
Sub2::solve();
}
// Sub2::solve();
#ifndef ONLINE_JUDGE
LocalTest::chkAns();
#endif
cout << "!";
for (int i = 1; i <= n; i++) cout << " " << a[i];
cout << endl;
}
#ifndef ONLINE_JUDGE
clock_t ed_clock = clock();
printf("time = %f ms\n", (double)(ed_clock - st_clock) / CLOCKS_PER_SEC * 1000);
printf("memory = %.2f MB\n", (&MEMORY_ED - &MEMORY_ST) / 1024.0 / 1024.0);
#endif
return 0;
}