CF2040D題解

Hanggoash發表於2024-12-10

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\) 的鏈,然後根據奇偶染色的原理也可以做。

不過我不太懂。

總結

構造題可以先構造出一種大體合法的方案,然後對於不合法的細節進行微調。