CF2040D題解
神奇構造題,感覺自己想到了一丟丟,但是沒有往正解的方向去想。
題意
給出一張 \(n\) 個節點的樹,從 \(1-2n\) 中最多每個數選一次填到樹中,求一種任意兩個節點權值之差都不是質數的填數方案。
分析
比賽的時候考慮到了 \(1-2n\) 這個特殊條件,可以考慮只用偶數或者是奇數,假設是隻用 \(2,4,6,8...,2n\) 的話,。
首先想到的是奇偶染色,然後分別把 \(2,6,10...(A)\) 和 \(4,8,12..(B)\) 填到不同顏色中。
然後發現這樣全是不合法,所以把這種想法叉掉了。。。。
我們發現實際上就可以從這個角度來思考,先考慮給這個樹分層,既然剛剛都順著填的思路是很錯誤的,那我們反過來,用一個 Meet in
the middle 的思路來進行染色。
也就是根據 bfs 序和層數的奇偶性來進行填充,對於所有奇數層,我們把 \(2,4,6,8..\) 按順序填進去;對於 所有的偶數層,我們把 \(2n,2n-2,2n-4...\) 按照順序填進去,這樣一定最後填完的時候不會重,並且大體上都是合法的,因為所有的相鄰差都是偶數,並且基本上都不是 \(2\)。那麼什麼地方會是 \(2\) 呢?肯定就是我們最後meet的地方,而且它一定在葉節點,並且只有這一個不合法的地方,我們直接對他進行 \(+-1\) 就可以了。
Code
#include<bits/stdc++.h>
using namespace std;
const int N=2e5+10;
int T,n;
inline void solve()
{
cin>>n;
vector<int> e[n+2],a(n+1,0),isf(n+1,0);
vector<vector<int>>num(2,vector<int>(n+2,0));
for(int i=1;i<=n;++i)num[0][i]=i<<1,num[1][i]=(n-i+1)<<1;
for(int i=1,u,v;i<n;++i)
{
cin>>u>>v;
e[u].push_back(v);
e[v].push_back(u);
}
queue<pair<int,int>> q;
int cnt[2]={0,0};
q.push({1,0});
while(q.size())
{
int x=q.front().first,type=q.front().second;
q.pop();
a[x]=num[type][++cnt[type]];
for(auto v:e[x])
{
if(a[v])continue;
isf[x]=1;
q.push({v,type^1});
}
}
for(int i=1;i<=n;++i)
{
if(isf[i])continue;
for(auto v:e[i])
{
if(abs(a[i]-a[v])==2)
{
if(a[i]>a[v])a[i]--;
else a[i]++;
goto OUTPUT;
}
}
}
OUTPUT:;
for(int i=1;i<=n;++i)cout<<a[i]<<' ';
cout<<'\n';
}
int main()
{
ios::sync_with_stdio(0);
cin.tie(0),cout.tie(0);
cin>>T;
while(T--)
{
solve();
}
return 0;
}
另一種解
這種奇怪構造應該可亂搞性挺高的,根據starsilk的說法,好像可以特判菊花,然後還是隻用偶數,找到一條長度為 \(4\) 的鏈,然後根據奇偶染色的原理也可以做。
不過我不太懂。
總結
構造題可以先構造出一種大體合法的方案,然後對於不合法的細節進行微調。