D. Playlist 思维
D. Playlist 思维
题目大意:
Arkady的播放列表最初包含 \(n\) 首歌曲,按照它们在播放列表中出现的顺序从 1到 \(n\) 编号。 Arkady从歌曲1开始开始逐一收听播放列表中的歌曲。播放列表循环播放,听完最后一首歌后,Arkady将从头开始继续听。
每首歌曲都有一个类型 \(a_i\),它是一个正整数。让Arkady完成听一首类型为 \(y\) 的歌曲,倒数第二首听过的歌曲的类型为 \(x\)。如果gcd(x,y)= 1,则从播放列表中删除最后收听的歌曲(类型y)。之后,他将继续正常收听,跳过已删除的歌曲并忘记他之前听过的歌曲。换句话说,删除歌曲后,他不能立即删除下一首歌曲。
比如:最开始是的列表是 \([5,9,2,10,15]\)
- 因为 \(gcd(5,9)=1\) 所以,删掉9,就变成 \([5,2,10,15]\)
- 虽然现在 \(gcd(5,2)=1\) 但是不会继续删掉 2,而是会去比较 \(gcd(2,10)\) 因为不是等于1,所以不会删掉10
- 继续比较 \(gcd(10,15)\) 也不会删掉
- 然后比较 \(gcd(15,5)\) 也不会删
- 然后比较 \(gcd(5,2)\) 因为等于1 ,所以删掉2
- 最后一致循环都不会删掉任意一个值,所以最后的序列是 \([5,10,15]\)
题解:
挺思维的。
- 首先发现进行一轮之后,下一轮需要判断的点,只有之前进行比较 \(gcd(x,y)=1\) 的 \(x\) (\(y\) 被删掉了)
- 所以只要把之前的 \(x\) 按照顺序放入一个队列中即可。
- 这个还有一个删的操作,这个只要把下一个位置用一个数组存下来即可。
#include <bits/stdc++.h>
using namespace std;
const int maxn = 2e5+10;
typedef long long ll;
int to[maxn],a[maxn],vis[maxn];
queue<int>que;
vector<int>ans;
void init(int n){
ans.clear();
while(!que.empty()) que.pop();
for(int i=0;i<n;i++) to[i] = (i+1)%n,vis[i] = 0;
}
int gcd(int a,int b){
return b==0?a:gcd(b,a%b);
}
void debug(int n){
for(int i=0;i<n;i++) printf("to[%d]=%d ",i,to[i]);
}
int main(){
int t;
scanf("%d",&t);
while(t--){
int n;
scanf("%d",&n);
init(n);
for(int i=0;i<n;i++) scanf("%d",&a[i]);
int id = -1;
for(int i=0;i<=n;i++){
if(id==-1) id = i;
else {
int v = i%n,now = gcd(a[id],a[v]);
if(now==1) {
vis[v] = 1;
ans.push_back(v+1),que.push(id);
to[id] = to[v],id = -1;
}
else id = i;
}
}
while(!que.empty()){
int u = que.front();que.pop();
if(vis[u]) continue;
int v = to[u],now = gcd(a[u],a[v]);
// printf("u = %d v = %d\n",u,v);
// debug(n);
if(now==1){
vis[v] = 1;
ans.push_back(v+1),que.push(u);
to[u] = to[v];
}
}
printf("%d",ans.size());
for(int i=0;i<ans.size();i++){
printf(" %d",ans[i]);
}
printf("\n");
}
return 0;
}