Codeforces Round 949 (Div. 2). D(构造+欧拉回路)
D. Turtle and Multiplication
题意
构造一个长度为 \(n(1\leq n \leq 10^6)\) 的数列 \({a_i}\) 使得:对于任意 \(1 \leq i,j < n(i \not = j)\) 都有 \(a_i*a_{i+1} \not = a_j*a_{j+1}\)。限制:\(1 \le a_i \le 3*10^5\)
你的任务是构造这个数列并且使得 \({a_i}\) 中的不同元素最少。
思考
我首先的思考是使得 \(a_i*a_{i+1}\) 的序列成 \(1,2,3,4,5,....\) 增加,发现这样极其难构造。
进而我思考,我先用已有的若干个数进行构造,构造到不能再往下写了,这时就会加入一个数,然后这个数又和前面的数重新组合,尽可能的多得再往后写一些。那么这个数与前面的每一个数都可以重新组合吗?换言之,这个数和前面的数重新组合时会不会出现已经出现过的数。
这时我发现如果新进来的是一个质数,那么这个数是可以和前面任意一个数重新组合而不重复的。进而我注意到,质数间两两组合是一定不重复的。于是我大胆猜想,这题只取质数。换言之,我们取若干个质数,用这若干个质数跑出最长的序列。
那么,如果我们取了n个质数,最长能跑多长呢?我们会发现,这n个质数取完一个到下一个,而且可以选任意一个,这样就构出了一张完全图(注意还可以自己到自己,也是可以的,要加入这个最长一笔画的贡献),而我们则是要在这张完全图上找到最长的一笔画。进一步我们能发现,如果点数 \(n\) 是奇数,也就是每个点的出边是偶数,那么这张图的边可以跑满,最长一笔画的长度在 \(n^2\) 左右,即 \(n\) 个质数可以构造出长度(\(|A|\)表示数列长度) \(|A| = n^2\) ,\(n\) 是偶数有点不一样,但也差不多。
估算了一下,3e5以内的质数大概在 28000 左右。\(28000^2 > 10^6\) 故这样构造一定可以通过这题。至此,思考完毕。
Solution
注意到质数两两相乘一定不同。故 \(a_i\) 只取质数。具体的,取若干个质数,任意两两连边形成一张完全图(包括自环)。
考虑 \(n\) 个点的完全图(包括自环)的最长一笔画长度:当 \(n\) 为奇数时,易得该完全图是欧拉图,故最长一笔画为 \(\frac{n(n-1)}{2}+n\);当 \(n\) 为偶数时,每个点的度数是奇数,这时删去 \(frac{n}{2}-1\) 条边,使得原图仅有两个度数是奇数的点,即半欧拉图,故最长一笔画为 \(\frac{n(n-1)}{2}+n-\frac{n}{2}+1\)
用 Hierholzer 算法求欧拉回路或欧拉通路,复杂度 \(O(n+m)\)
Code
Talk is cheap.Show me the code.
#include<bits/stdc++.h>
using namespace std;
inline int read() {
int x = 0, f = 1; char ch = getchar();
while(ch<'0' || ch>'9') { if(ch=='-') f=-1; ch=getchar(); }
while(ch>='0'&&ch<='9') { x=(x<<3)+(x<<1)+(ch^48); ch=getchar(); }
return x * f;
}
const int N = 2e3+7;
const int M = 3e5+7;
int A,n;
int cur[N];
int E[N][N];
vector<int> P;
bool vis[M];
void Init() {
P.push_back(0);
for(int i=2;i<M;++i) {
if(!vis[i]) {
P.push_back(i);
for(int j=2;i*j<M;++j) {
vis[i*j] = 1;
}
}
}
}
int f(int x) {
return (x%2 ? x*(x-1)/2+x+1 : x*(x-1)/2+x-x/2+1+1);
}
int flag;
vector<int> a;
void Dfs(int u) {
for(int i=cur[u]+1;i<=n;++i) {
i = max(i, cur[u]+1);
if(E[u][i]) {
cur[u] = i;
E[u][i] = E[i][u] = 0;
Dfs(i);
}
}
a.push_back(P[u]);
}
void solve() {
n = 1;
A = read();
while(f(n) < A) ++n;
for(int i=1;i<=n;++i) {
for(int j=1;j<=n;++j) {
E[i][j] = 1;
}
}
if(n%2 == 0) {
for(int i=3;i<=n;i+=2) {
E[i][i+1] = 0;
E[i+1][i] = 0;
}
}
Dfs(1);
for(int i=0;i<A;++i) printf("%d ",a[i]);
cout << endl;
for(int i=1;i<=n;++i) {
for(int j=1;j<=n;++j) {
E[i][j] = 0;
}
}
for(int i=1;i<=n;++i) {
cur[i] = 0;
}
a.clear();
}
int main()
{
Init();
int T = read();
while(T--) {
solve();
}
return 0;
}
Summary
补一下 Hierholzer 算法,不是很懂怎么证的。