CF1981D題解
前言
標籤:篩法,尤拉回路。
賽後過的,構造一眼秒,尤拉圖寫錯了,多少有點抽象。
題意
構造一個長度為 \(n\) 的序列 \(a\),需要滿足:
-
\(\forall 1 \le i \le n\),\(1 \le a_i \le 3\times10^5\)。
-
\(\forall 1 \le i<j<n\),\(a_i\times a_{i+1}\ne a_j\times a_{j+1}\)。
分析
發現任意兩個質數的乘積是唯一的,而 \(\max_{1\le i\le 3\times 10^5}\{d(i)^2\} \le 10^6\)。
所以顯然只需要選 \(m\) 個質數。
而現在就是要構造一個長度為 \(n\) 的序列使得每一對無序數對 \(\{a_i,a_{i+1}\}\) 都不同。
因為構造方式是從一個數開始,走到另一個數,和路徑是一樣的,所以考慮構造一個無向完全圖(帶自環),然後找最長路徑。
當 \(m\) 是奇數時,每個點的度一定是偶數,所以直接跑尤拉回路,序列長度為 \(\frac{m\times (m+1)}{2}+1\)。
當 \(m\) 是偶數,每個點的度一定是奇數,我們可以刪除所有形如 \(\{3,4\},\{5,6\},\dots,\{n-1,n\}\) 的邊,這樣就剩下了兩個奇點,直接跑尤拉回路,序列長度為 \(\frac{m\times (m+1)}{2}-\frac{m-2}{2}+1\)。
直接列舉求 \(m\) 即可。
程式碼
建圖用了 bitset
。
#include<bits/stdc++.h>
using namespace std;
inline int read(){
char ch=getchar();int x=0;bool f=1;
while(ch<'0'||'9'<ch){if(ch=='-')f=0;ch=getchar();}
while('0'<=ch&&ch<='9'){x=(x<<1)+(x<<3)+ch-'0';ch=getchar();}
return f?x:-x;
}
const int N=3e5+2;
int cnt,prime[N];
bool is_p[N];
void init(int n){
for(int i=2;i<=n;i++){
if(!is_p[i])prime[++cnt]=i;
for(int j=1;j<=cnt&&i*prime[j]<=n;j++){
is_p[i*prime[j]]=1;
if(!(i%prime[j]))break;
}
}
}
const int M=1500;
int n,m;
bitset<M>vis[M];
stack<int>st;
void dfs(int u){
if(vis[u][u])vis[u][u]=0,dfs(u);
for(int i=vis[u]._Find_first();i<=m;i=vis[u]._Find_first())
if(vis[u][i])
vis[u][i]=vis[i][u]=0,dfs(i);
st.push(prime[u]);//注意要最後加點,不然跑出來的不是尤拉回路,筆者栽在這裡了
}
void Main(){
n=read();
if(n==1)return puts("1"),void();
for(m=1;(m*(m+1)/2)-((m&1)?0ll:((m>>1)-1))<n-1;m++);
for(int i=1;i<=m;i++)
vis[i].set();
if(!(m&1))
for(int i=3;i<=m;i+=2)vis[i][i+1]=vis[i+1][i]=0;
dfs(1);
while(!st.empty()&&n)printf("%d ",st.top()),st.pop(),n--;
while(!st.empty())st.pop();
puts("");
return;
}
signed main(){
init(13000);
int T=read();
while(T--)Main();
return 0;
}