Post

CF1770(div2)题解(Good Bye 2022)

CF1770

A. Koxia and Whiteboards

题意:
给定长度为n的数组a,m次操作,第i次操作用 $b_i$ 替换数组任意数,求数组最后的最大总和
思路:
优先队列建小根堆,每次弹出最小值,加入 $b_i$

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
void solve(){
	int n,m;
	cin>>n>>m;
	priority_queue<int,vector<int>,greater<int>> q;
	ll sum=0;
	for(int i=1;i<=n;i++){
		int x;
		cin>>x;
		q.push(x);
		sum+=x;
	}
	for(int i=1;i<=m;i++){
		int x;
		cin>>x;
		sum-=q.top();
		q.pop();
		q.push(x);
		sum+=x;
	}
	cout<<sum<<"\n";
}

B. Koxia and Permutation

题意: 给定一个长度为n数组a(permulation),一个整数k
构造数组c
$c_i=max(a_i,a_{i+1},…,a_{i+k-1})+min(a_i,a_{i+1},…,a_{i+k-1})$
构造代价为 $max(c_1,c_2,…,c_{i+k-1})$
求最小代价的数组a 思路:
易得最小代价为n+1
每次最大一个数最小一个数依次输出即可

1
2
3
4
5
6
7
8
9
void solve(){
	int n,k;
	cin>>n>>k;
	for(int i=1,j=n;i<=j;i++,j--){
		if(i!=j)cout<<j<<" "<<i<<" ";
		else cout<<i<<" ";
	}
	cout<<"\n";
}

Koxia and Number Theory(数论+中国剩余定理+鸽巢原理)

题意:
对数组每个数加上x,使数组两两互质gcd=1,输出YES或NO
思路:
如果有相同的数,输出NO
考虑奇偶性对数组的影响,若奇数和偶数均大于等于2个,则当选择加奇数时,原来的若干对奇数能被2整除;若选择加偶数,则原来的若干对偶数能被2整除
将2推广到一般质数p
定义 $cnt_j$ 为数组模p后为j的个数
若 $min(cnt_0,cnt_1,…,cnt_{p-1})>=2$ 则输出NO 鸽巢原理:只需要枚举 $\lfloor \frac{n}{2} \rfloor $ 个质数
证明: 因为 $min(cnt_0,cnt_1,…,cnt_{p-1})>=2$,
且 $n=cnt_0+cnt_1+cnt_{p-1} $,
所以 $n>=2 \cdot (p-1)$
所以只需枚举小于等于 $\frac{n}{2}$ 的质数

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
void solve(){
	int n;
	cin>>n;
	vector<ll> a(n+1);
	int one=0,two=0;
	map<ll,bool> vis;
	bool g=0;
	for(int i=1;i<=n;i++){
		cin>>a[i];
		if(vis[a[i]])g=1;
		vis[a[i]]=1;
	}
	if(g){
		cout<<"NO\n";
		return;
	}
	for(int i=2;i<=n/2;i++){
		vector<int> cnt(i);
		for(int j=1;j<=n;j++)cnt[a[j]%i]++;
		if(*min_element(all(cnt))>=2){
			cout<<"NO\n";
			return;
		}
	}
	cout<<"YES\n";
}

D. Koxia and Game(集合(并查集)+bfs)

题意:
A,B两个人在玩游戏,有a,b,c三个数组长度为n,a,b给定,游戏有n轮,第i轮A先从 $a_i,b_i,c_i$ 选一个数,B再从剩下里面选一个数,n轮后B选的数包含1-n的所有数,求c有多少种
思路:
注意1:每一轮剩下的两个数必须相同,才能固定B的选法
每一轮从a,b选出一个数能组成1-n的数
将 $(a_i,b_i)$ 看成一条边
每个数可以分成多个集合, 建双向边,遍历完一个环后每个节点的边数总和为edge,节点个数ver
当且仅当edge/2==ver时,才表示节点能遍历完形成闭环且不影响其他数字成环,否则直接输出0 在闭环中,遍历方法有两种(顺时针和逆时针),ans=2;
如果环内有自环,当前节点有n种可能,环的遍历方式只有一种了,ans/=2,ans
=n

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
ll ksm(ll a){
	ll ans=1;
	ll b=mod-2;
	for(;b;b>>=1){
		if(b&1)ans=(ans*a)%mod;
		a=(a*a)%mod;
	}
	return ans%mod;
}
void solve(){
	int n;
	cin>>n;
	vector<int> a(n+1),b(n+1);
	for(int i=1;i<=n;i++)cin>>a[i];
	for(int i=1;i<=n;i++)cin>>b[i];
	vector<int> adj[n+1];
	for(int i=1;i<=n;i++){
		adj[a[i]].pb(b[i]);
		adj[b[i]].pb(a[i]);
	}
	vector<int> vis(n+1);
	ll ans=1;
	for(int i=1;i<=n;i++){
		if(vis[i])continue;
		vis[i]=1;
		queue<int> q;
		q.push(i);
		int edge=0,ver=0;
		while(!q.empty()){
			int x=q.front();
			q.pop();
			edge+=adj[x].size();
			ver++;
			for(auto y:adj[x]){
				if(!vis[y]){
					vis[y]=1;
					q.push(y);
				}
			}
		}
		edge/=2;
		if(edge!=ver){
			cout<<"0\n";
			return;
		}
		ans*=2;
		ans%=mod;
	}
	int n2=ksm(2);
	for(int i=1;i<=n;i++){
		if(a[i]==b[i]){
			ans*=n2;
			ans%=mod;
			ans*=n;
			ans%=mod;
		}
	}
	cout<<ans<<"\n";
}
This post is licensed under CC BY 4.0 by the author.