【Virt.Contest】CF1215(div.2)

第二次打虚拟赛。

CF 传送门

T1:Yellow Cards

黄色卡片

中规中矩的 \(T1\)

首先可以算出一个人也不罚下时发出的最多黄牌数: \(sum=a1*(k1-1)+a2*(k2-1)\)

此时,若 \(sum>=n\),则最少罚下 \(0\) 人,否则最少罚下 \(n-sum\) 人。

然后看最多。优先考虑全部发给所需黄牌数少就能下场的一队。如果全下光了,就去发给另一队。

不得不说这比赛机制简直找打

Code:

#include<bits/stdc++.h>
using namespace std;
int a1,a2,k1,k2,n,sum,tmp; 
int main(){
	scanf("%d%d%d%d%d",&a1,&a2,&k1,&k2,&n);
	sum=a1*(k1-1)+a2*(k2-1);
	printf("%d ",max(0,n-sum));
	if(k1>k2){
		swap(k1,k2);
		swap(a1,a2);
	}
	tmp=n/k1;
	if(tmp>a1){
		n-=a1*k1;
		printf("%d",a1+n/k2);
	}
	else printf("%d",tmp);
	return 0;
}

\(5min\) 速切

T2:The Number of Products

一开始想的是用前缀和算每个区间的负数个数,但是 \(O(n^2)\) 显然超时。于是就顿住了 \(qwq\)

后来想到可以对每一次读入快速判。设置一个标记记录当前正负状态。如果标记为正,那么正答案加上之前所有的正区间、负答案加上之前所有负区间(不变号)。如果标记为负,那么正答案加上之前所有的负区间、负答案加上之前所有正区间(变号)。

然鹅样例都过不了,一度怀疑做法假了。就去看了眼 \(T3\)。后来发现,没有算单独一个数的。最后正答案再加上正数个数、负答案再加上负数个数就好了。时间复杂度 \(O(n)\)

Code:

#include<bits/stdc++.h>
using namespace std;
long long n,a,z,f,ans1,ans2,fl=1;
int main(){
	scanf("%lld",&n);
	for(int i=1;i<=n;i++){
		scanf("%lld",&a);
		if(a<0) fl=-fl;
		if(fl>0) ans1+=z,ans2+=f,z++;
		else ans1+=f,ans2+=z,f++;
	}
	printf("%lld %lld",ans2+f,ans1+z);
	return 0;
}

\(33min\) 才过 \(qwq\)

T3:Swap Letters

一开始读错题,以为是单个字符串内部交换。心想怎么会这么水。对着样例才发现了错误。

首先容易得知,两个字符串中 \(b\)(或 \(a\)) 的个数为偶数时,一定有解。为奇数则一定无解。

其次考虑怎么交换。对照样例三:

in:

8
babbaabb
abababaa

out:

3
2 6
1 3
7 8

发现,每一对交换的字符有共同点

  • 要不是串一都为 \(a\),串二都为 \(b\) 的一对

  • 要不是串一都为 \(b\),串二都为 \(a\) 的一对

简单思考后发现这样成对交换就是最优的。(换一次就可以匹配上两位)

于是,考虑先统计出 串一为 \(a\),串二为 \(b\) 的位数 \(cnt1\),并存入 \(ans1\) 数组。同时统计出 串一为 \(b\),串二为 \(a\) 的位数 \(cnt2\),并存入 \(ans2\) 数组。

这时发现一个问题,\(cnt1\)\(cnt2\) 不一定为偶数,有可能不能各自成对匹配完。但可以发现, \(cnt1\)\(cnt2\) 必同奇偶。由于偶数成对匹配更优,所以只可能剩下 一位串一为 \(a\),串二为 \(b\)一位串一为 \(b\),串二为 \(a\)
这时就出现了样例一的情况:

in:

4
abab
aabb

out:

2
3 3
3 2

只要按着样例一的方式特判输出即可。

Code:

#include<bits/stdc++.h>
using namespace std;
int n,ans1[200005],ans2[200005],cnt,cnt1,cnt2;
string s1,s2;
int main(){
	scanf("%d",&n);
	cin>>s1>>s2;
	for(int i=0;i<n;i++){
		if(s1[i]=='b') cnt++;
		if(s2[i]=='b') cnt++;
	}
	if(cnt%2==1) printf("-1");
	else{
		for(int i=0;i<n;i++){
			if(s1[i]=='a'&&s2[i]=='b') ans1[++cnt1]=i+1;
			if(s2[i]=='a'&&s1[i]=='b') ans2[++cnt2]=i+1; 
		}
		if(cnt1%2==0){
			printf("%d\n",cnt1/2+cnt2/2);
			for(int i=1;i<=cnt1;i+=2){
				printf("%d %d\n",ans1[i],ans1[i+1]);
			}
			for(int i=1;i<=cnt2;i+=2){
				printf("%d %d\n",ans2[i],ans2[i+1]);
			}
		}
		else{
			printf("%d\n",cnt1/2+cnt2/2+2);
			for(int i=1;i<=cnt1-1;i+=2){
				printf("%d %d\n",ans1[i],ans1[i+1]);
			}
			for(int i=1;i<=cnt2-1;i+=2){
				printf("%d %d\n",ans2[i],ans2[i+1]);
			}
			printf("%d %d\n",ans1[cnt1],ans1[cnt1]);
			printf("%d %d\n",ans1[cnt1],ans2[cnt2]);
		}
	}
	return 0;
}

\(49min\) AC \(qwq\)

T4:Ticket Game

看上去是高大上的博弈论问题,实际就是贪心。

考虑把字符串分成左右两部分,分别统计出两边的 ? 个数 \(l,r\) 与不是 ? 的数字之和 \(ls,rs\)

因为 Monocarp 先手,而 Bicarp 要维护字符串两边和相等,所以在两边都还有 ? 时,Monocarp 填任意一个, Bicarp 必然在另一边填一个相等的数字。所以,这些过程对结果是无意义的,可以把两边都有的 ?个数抹掉。

于是,问题就变成了一边都是数,在另一边填数。由题意,问号为偶数个,所以原来两边问号奇偶相同,减掉后剩下的问号也为偶数个。

这时我们发现,问题很像小学奥数里的取棋子游戏。就是每人每次取一个或两个,取到最后一个者胜,判断先后手谁胜。易知,棋子总数为 \(3\) 的倍数时,后手必胜,因为只要补 \(3\) 即可。

假设剩下的 ? 都在右边,问题就变成两人轮流填 \(0-9\) 的数,若最后和为 \(ls-rs\) 则后手胜。所以易想到 “补 \(9\)” 的策略。因为问号只有 \(r-l\) 个,所以每人只能填 \(\frac{r-l}{2}\) 次。

得出结论:只要 \(ls-rs\)\(9\)\(\frac{r-l}{2}\) 倍,则 Bicarp 胜。反之则 Monocarp 胜。

因为赛时很快想到思路,代码实现也简单,所以成为拉分的一题。

Code:

#include<bits/stdc++.h>
using namespace std;
int n,ls,rs,l,r;
string s;
int main(){
	scanf("%d",&n);
	cin>>s;
	for(int i=0;i<n/2;i++){
		if(s[i]=='?') l++;
		else ls+=s[i]-'0';
	}
	for(int i=n/2;i<n;i++){
		if(s[i]=='?') r++;
		else rs+=s[i]-'0';
	}
	if(l>r){
		swap(l,r);
		swap(ls,rs);
	}
	r-=l;
	if(r/2*9+rs==ls) printf("Bicarp");
	else printf("Monocarp");
	return 0;
}

\(1h\) \(05min\) AC \(qwq\)

T5:Marbles

状压 \(DP\),告辞

一个结论:把一个序列变为不降所需要的相邻元素交换次数就是这个序列的逆序对数。

那么这题要我们干的是什么呢?就是按颜色变为不降,只不过颜色的大小顺序我们可以任意指定而已。比如样例 \(1\) 指定的就是 \(3<4<2\),样例 \(2\) 指定的就是 \(20<1<14<10<2\)

看到颜色数最多 \(20\),显然是状压 \(DP\)。定义 \(f[s]\)\(s\) 对应的颜色的大小顺序已经安排好了的最小逆序对数。

假设已经安排好 \(k1<k2<⋯<km\) ,我们选择一个在 \(s\) 中没出现过的 \(k_{m+1}\)。那么新的状态就要加上 \((k_1,k_{m+1})\)之间的,满足 \(i<j,a_i=k_1,a_j=k_2\)\((i,j)\)对数。这个预处理一下就好了。

——来自 \(\color{orange}{x义x}\) 巨佬的 题解

Code:

#include<bits/stdc++.h>
#define ll long long
using namespace std;
int n,a[400005],cnt[25];
ll b[25][25],f[2000005];
int main(){
	scanf("%d",&n);
	for(int i=1;i<=n;i++){
		scanf("%d",&a[i]);
		for(int j=1;j<=20;j++) b[j][a[i]]+=cnt[j];
		cnt[a[i]]++;
	}
	memset(f,0x3f,sizeof(f));
	f[0]=0;
	for(int k=0;k<(1<<20);k++){
		for(int i=1;i<=20;i++){
			if((k>>(i-1))&1) continue ;
			ll tmp=0;
			for(int j=1;j<=20;j++){
				if((k>>(j-1))&1) tmp+=b[j][i];
			}
			f[k|(1<<(i-1))]=min(f[k|(1<<(i-1))],f[k]+tmp);
		}
	}
	printf("%lld\n",f[(1<<20)-1]);
	return 0;
}

T6:Radio Stations

施工中 \(qwq\)

posted @ 2022-08-27 12:53  Binary_Lee  阅读(25)  评论(0编辑  收藏  举报
Title