1867D - Cyclic Operations
题意:给定一个长度为 n 的数组b一个数字k,同时有一个a数组,起初a数组全部为0。你可以进行无限次以下操作:构造出一个长度为k的l数组,其中令 。 要求问最终能否使得a与b数组相等。
思路:给定表达式很容易想到环当中的内容:环的置换、其定义如下:
其中,本题目的l数组既是图中的()。只不过将
放到了最后。
接下来考虑如何才能满足环上的轮换:当需要对环内的数进行操作时,必须要满足轮换的阶数恰好等于整个环当中的元素个数。例如:样例
4 2
2 4 3 1 , 此时(2 4 1) 是一个环内的 , 其意义代表了2 -> 4有一条边,4 ->1 有一条边,1 ->2有一条边。如果轮换阶数为2 ,每次都只会构造出一个二元换, 那么无论如何也无法构造出这样的三元环。同理4也是一样。因此必须要满足 k = 环的元素个数。
如果不在环内的数,因为可以无限次操作,可以先借用他所连通的环中的数,先将环外的数处理完成,最终再修改环内的数。因此不在环内的数默认全部能够修改成功。
因此,本题变成了一个有向图求环中元素个数的题,用拓扑排序完成即可(k = 1 时只能构造出自环,环外的点都无法满足,因此需要特判一下)。
void solve()
{
int n , k;
cin>>n>>k;
int a[n + 5];
vector<int>tr[n + 5];
int in[n + 5];
memset(in , 0 ,sizeof in);
for(int i = 1 ; i <= n ; i++){
cin>>a[i];
tr[i].pb(a[i]);
tr[a[i]].pb(i);
in[a[i]] ++;
}
bool vis[n + 5];
int FF = 0;
memset(vis , false , sizeof vis);
for(int i = 1 ; i <= n ; i ++){
if(a[i] == i){
vis[i] = 1;
FF = 1;
}
}
if(FF){
if(k != 1){
cout<<"NO\n";
return;
}
else{
for(int i = 1 ; i <= n ; i++){
if(!vis[i]){
FF = 0;
}
}
if(!FF){
cout<<"NO\n";
}
else{
cout<<"YES\n";
}
return;
}
}
for(int i = 1 ; i <= n ; i ++){
vector<int>v;
queue<int>q;
if(vis[i] == 0){
v.pb(i);
q.push(i);
while(!q.empty()){
int x = q.front();
vis[x] = 1;
q.pop();
for(auto it : tr[x]){
if(vis[it] == 0){
vis[it] = 1;
v.pb(it);
q.push(it);
}
}
}
for(auto x : v){
if(in[x] == 0){
q.push(x);
}
}
while(!q.empty()){
int x = q.front();
q.pop();
in[a[x]] --;
if(in[a[x]] == 0){
q.push(a[x]);
}
}
vector<int>res;
for(auto x : v){
if(in[x] > 0){
res.pb(x);
}
}
if(res.size() != k){
cout<<"NO\n";
return;
}
}
}
cout<<"YES\n";
}