Codeforces Round 897 (Div. 2)(D :环的轮换)

1867D - Cyclic Operations 

        题意:给定一个长度为 n 的数组b一个数字k,同时有一个a数组,起初a数组全部为0。你可以进行无限次以下操作:构造出一个长度为k的l数组,其中令a_{l_{i}} = l_{i\%k + 1} 。 要求问最终能否使得a与b数组相等。

        思路:给定表达式很容易想到环当中的内容:环的置换、其定义如下:

 其中,本题目的l数组既是图中的(i_{2}i_{3}...i_{k}i_{1})。只不过将 i_{1} 放到了最后。

        接下来考虑如何才能满足环上的轮换:当需要对环内的数进行操作时,必须要满足轮换的阶数恰好等于整个环当中的元素个数。例如:样例

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";
}            

  • 3
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值