1. 程式人生 > 其它 >Codeforces Round #733 (Div. 1 + Div. 2) D. Secret Santa

Codeforces Round #733 (Div. 1 + Div. 2) D. Secret Santa

D. Secret Santa

題意

給t組樣例
每組樣例給n個數
a[1] , a[2] , a[3] ...... a[n]
(t組樣例n的總和<=2e5,a[i] <= n)
並且保證a[i] != i
求一個數組p
並且這個陣列p為1到n的全排列中的一種方式
求 p[i] == a[i] 的個數最大並且p[i] != i

輸出這個個數的最大值和p陣列

思路

首先先把可以匹配的匹配了
舉個例子
a 6 4 6 2 4 5 1
從頭到尾掃一遍
p 6 4 _ 2 _ 5 1

還差3 7沒填
然後在考慮怎麼安排3 7的位置 使得p[i] != i 
很明顯應該
p 6 4 7 2 3 5 1

也就是說題目轉換成為了
怎麼安排未匹配的數
使得p[i] != i 

這裡有個性質
自閉了1h才想出來

假設現在沒有匹配的數是 1 2 3 4 5 6 7 
有7個位置要填
_ _ _ _ _ _ _ 

那我1 2 3 4 5 6 7 正序填
7個都衝突出現了p[i] = i 的情況

如果倒敘填
是 7 6 5 4 3 2 1 
只有p[4] 衝突了

那是不是說明了正序填和倒敘填有一種情況
最多隻衝突一次
(可以自己多找幾組樣例看看是不是對的)

如果衝突一次
找到這個數交換一下即可

時間複雜度:O n

#include<bits/stdc++.h>
#define fer(i,a,b) for(re i = a ; i <= b ; ++ i)
#define re register int
#define pll pair<int,int> 
#define x first 
#define y second 
#define sf(x) scanf("%d",&x)
#define sfl(x) scanf("%lld",&x)
typedef long long ll ;
using namespace std;
const int N = 2e5 + 10 , M = 2010 , inf = 0x3f3f3f3f , mod = 1e9 + 7 ;
int t ;
int n ;
int a[N] ; // a陣列
bool st[N] ; // 判斷這個數有沒有出現過
int ans[N] ;  
int s[N] ; // s , ans最後的答案陣列
int main()
{
    cin >> t ;
    while(t--)
    {
        cin >> n ;
        
        fer(i,1,n) sf(a[i]) ;
        
        fer(i,1,n) st[i] = ans[i] = 0 ; // 初始化
        
        int cnt = 0 ;
        
        fer(i,1,n) // 把可以匹配的先匹配了
        {
            if(!st[a[i]])
            {
                cnt ++ ;
                st[a[i]] = 1 ;
                ans[i] = a[i] ;
            }
        }
        
        vector<int> q ; // 找到未匹配的數
        fer(i,1,n)
        {
            if(!st[i])
                q.push_back(i) ;
        }
        //fer(i,1,n) cout << a[i] << " " ;
        
        sort(q.begin(),q.end()) ;
        reverse(q.begin(),q.end()) ;
        // 倒敘填一遍
        
        int cnt1 = 0 , cnt2 = 0 ;
        // cnt1為倒敘的衝突個數 , cnt2反之
        
        fer(i,1,n)
        {
            s[i] = ans[i] ;
        }
        // 先讓s陣列和ans陣列相等
        
        // 倒敘填ans陣列
        int k = 0 ;
        fer(i,1,n)
        {
            if(!ans[i])
            {
                ans[i] = q[k ++] ;
            }
        }
        
        fer(i,1,n)
        {
            if(ans[i] == i) cnt1 ++ ;
        }
        
        // 正序填s陣列
        reverse(q.begin(),q.end()) ;
        k = 0 ;
        fer(i,1,n)
        {
            if(!s[i])
            {
                s[i] = q[k ++] ;
            }
        }
        
        fer(i,1,n)
        {
            if(s[i] == i) cnt2 ++ ;
        }
        
        if(cnt1 > cnt2)
        {
       	// 如果ans陣列衝突值大了,把s陣列給ans
            fer(i,1,n)
            {
                ans[i] = s[i] ;
            }
        }
        
        // 找到這個衝突值的下標
        int xia = 0 ;
        fer(i,1,n)
        {
            if(ans[i] == i)
            {
                xia = i ;
                break ;
            }
        }
        
        // 然後交換
        fer(i,1,n)
        {
            if(ans[i] == a[xia] && i != xia)
            {
                swap(ans[i],ans[xia]) ;
                break ;
            }
        }
        
        // 最後輸出答案
        cout << cnt << "\n" ;
            
        fer(i,1,n) cout << ans[i] << " " ;
        
        cout << "\n" ;
    }
    return 0;
}