計蒜客/小教官(xjb)
題目鏈接:https://nanti.jisuanke.com/t/366
題意:中文題誒~
思路: 先通過給出的條件構造一個符合題意的數組(可以是任意一個符合條件的數組,菜雞不會證明;
然後構造的數組和初始序列1, 2, 3, 4...n最少不同元素的個數就是答案;
這點是比較好理解的:題目中給出的b1, b2, ...bm可以是不連續的, 那麼如果每次選擇的m個與初始序列不同位置的元素並且通過一次操作後可以到達初始序列所在位置;
那麼所需代價肯定是最小的,總代價即爲位置不同的元素的數目. 所有情況都可以分解爲m爲 2 或 3的情況的組合,而對於m爲2, 3的情況前面所述顯然是正確的;
那麼剩下的問題就是求目標序列和構造序列最少多少個元素不同了,注意這裏的序列是循環序列;
對於循環序列, 並不確定其開頭元素是那個,枚舉其開頭元素的話,時間復雜度爲O(n^2), 顯然會tle;事實上也並不需要那樣做,可以先求最多有多少個元素與初始序列位置相同;
再用n減一下即可. 注意這裏的初始序列是一個特殊的序列,爲1, 2, 3, 4...n, 那麼可以用(a[i]-i+n)%n表示其相對序列,相對序列號相同的元素一定存在某個開頭元素使其位置與
初始序列是相同的, 所以只要找出最多的相對序列號相同的元素數目即爲對多與初始序列位置相同的元素的個數;
注意還要逆時針再計算一邊相對序列號相同的最多元素數目;
代碼:
1 #include <iostream> 2 #include <stdio.h> 3 #include <string.h> 4 using namespace std; 5 6 const int MAXN=5e4+10; 7 int a[MAXN], vis[MAXN]; 8 struct node{ 9 int f, s; 10 }gg[MAXN]; 11 12 int main(void){ 13 int n; 14 scanf("%d", &n); 15 for(int i=1; i<=n; i++){ 16 scanf("%d%d", &gg[i].f, &gg[i].s); 17 } 18 a[1]=1; 19 vis[1]=true; 20 int indx=1, cnt=1; 21 while(1){//構造序列 22 int cc=gg[cnt].f; 23 if(vis[cc]){ 24 cc=gg[cnt].s; 25 if(vis[cc]) break; 26 } 27 vis[cc]=true; 28 a[++indx]=cc; 29 cnt=cc; 30 } 31 if(indx<n){ 32 cout << -1 << endl; 33 return 0; 34 } 35 indx=0; 36 while(indx<n){//變成下標從0開始,便宜後面計算 37 a[indx]=a[indx+1]; 38 indx++; 39 } 40 memset(vis, 0, sizeof(vis)); 41 int cc=0; 42 for(int i=0; i<n; i++){//順時針計算相對序號 43 int temp=(n+a[i]-i)%n; 44 vis[temp]++; 45 } 46 for(int i=0; i<n; i++){ 47 cc=max(cc, vis[i]); 48 } 49 memset(vis, 0, sizeof(vis)); 50 for(int i=0; i<n; i++){//逆時針計算相對序號 51 int temp=(n+a[n-1-i]-i)%n; 52 vis[temp]++; 53 } 54 for(int i=0; i<n; i++){ 55 cc=max(cc, vis[i]); 56 } 57 printf("%d\n", n-cc); 58 return 0; 59 }
我就是我,颜色不一样的烟火 --- geloutingyu