Brief Description
给你一个 \(n\) 位的 \(p\) 进制数,第 \(i\) 位为 \(a_i\)。
请问最少要让该数加多少次 \(1\),可以让数码 \(0,\cdots,p−1\) 都出现过(包含在中间过程出现)。
Solution
因为是 \(p\) 进制,不难发现答案一定不会超过 \(p−1\),也就是说在最坏情况下就是其最后一位加至多 \(p−1\) 次才可以使得 \(0, \cdots ,p-1\) 每一个数码都出现一次。
然而题目给的数有 \(n\) 位,是有一定量的数码是出现过的。
所以显然可以考虑分进位与不进位分类讨论。
进位的条件其实就是存在小于它的数且它没有出现过。
这样讨论完了之后假设第一位为 \(x\) 那么小于等于 \(x\) 的都出现过,那么我们只需要找到一个最大的且没有出现过的数让 \(x\) 加到这个数就好了。
Code
#include<bits/stdc++.h>
using namespace std;
const int N = 5e6+5;
int a[N];
map<int,bool> vis;
int main(){ int t; scanf("%d",&t); while(t--){ int n,p; scanf("%d%d",&n,&p); for(int i=1; i<=n; i++) scanf("%d",&a[i]); for(int i=1; i<=n; i++) vis[a[i]] = true; reverse(a+1,a+n+1); bool flag = true; for(int i=a[1]; i>=max(a[1]-300,0); i--) if(!vis[i]) flag = false; set<int> st; int ans = 0; int limit = p - 1; if(!flag){ ans += p - 1 - a[1] + 1; limit = a[1] - 1;a[1] = 0; a[2]++; for(int i=2; i<=n; i++){ if(a[i] == p) a[i] = 0,a[i+1]++; } } for(int i=1; i<=n; i++) vis[a[i]] = true; if(a[n+1]) vis[a[n+1]] = true; for(int i=limit; i>=max(limit-300,0); i--){ if(!vis[i]){ ans = ans + i - a[1]; break; } } printf("%d\n",ans);vis.clear(); for(int i=1; i<=n+1; i++) a[i] = 0; } return 0;
}