CF构造思维题练习(1200-1400)

 

目录

CF1706A Another String Minimization Problem 1

CF1705B Mark the Dust Sweeper 2

CF1700B Palindromic Numbers 3

 CF1659A Red Versus Blue 4

k-LCM (easy version) - 洛谷 5

CF1497C2 k-LCM (hard version) 6

CF1693A Directional Increase 7

CF1694B Paranoid String 8

CF1681C Double Sort 9

 CF1692F 3SUM 10

 CF1550A Find The Array 11


CF1706A Another String Minimization Problem 1

首先由贪心。①改动的字符越多越好 ②A越前面越好

基于这两点,先对两个位置去最小值,如果这个位置已经改动过了,那么不要浪费,去改动另一个更大但是可能没动过的点。

        inln(n, m);
        vector<int>arr;
		inln(arr, n);
		vector<int>ans(100, 1);
		unordered_map<int, int>mp;
		rep(j, 1, n) {
			int t1 = max(arr[j], (m + 1 - arr[j])), t2 = min(arr[j], (m + 1 - arr[j]));
			if (mp[t2] == 0) {
				ans[t2] = 0;
				mp[t2] = 1;
			}
			else {
				ans[t1] = 0;
			}
		}
		rep(j, 1, m) {
			cout << (char)(65 + ans[j]);
		}
		pln("");

CF1705B Mark the Dust Sweeper 2

 首先原数列都是>=0的。大于0条件不考虑n,所以可以把n看作垃圾桶,前面所有的数都倒入第n个元素,假设每个数到第n个数之间都是大于0的,那么我们可以从左边将i值减为0,而n加上i,操作数+i。但是不可能每个数到第n个数之间都是大于0的,所以对之间的0特判一次操作,也就是肯定要分出操作来将0处变为1.因为不管证明操作n之前的元素总和都没变,所以答案就是将所有数减完的操作数sum,加上需要填的0的个数。

还要注意,如果一开始元素都是0就不需要计算了,从去掉前导0的元素开始计算。

        cin >> n;
		vector<int>arr;
		inln(arr, n);
		int ok = 0;
		int ans = 0;
		int cnt = 0;
		rep(j, 1, n) {

			if (arr[j] )
				ok = 1;
			if (ok &&arr[j]==0&& j != n)//去除前导0
				cnt++;
		}
		pln(accumulate(arr.begin() + 1, arr.end() - 1,(int) 0) + cnt);

 

CF1700B Palindromic Numbers 3

 简单构造,如果这个数的开头不是9,那么设置回文数为9...9(n位),则满足要求的一个数则为9..9-n

如果这个数开头为9,那么相减则位数-1不满足题意,此时设置回文数为1...1(n+1位)

for _ in range(int(input())):
    l=int(input())
    n=int(input())
    tar="9"*l
    if str(n)[0]=='9':
        t='1'*(l+1)
        print(int(t)-n)
    else:
        print((int(tar))-n)

 CF1659A Red Versus Blue 4

 题意也就是给定r个R和b个B,(r>b)问怎么排列会使连续的R和B最短。 

可知将整个串分为b+1份时,间隔均匀插入R连续串会最短。所以就间隔插入r/b个R,剩下的r%b余数分配给每一份。

        int r, b;
		inln(n, r, b);
		int x = r / (b + 1), y = r % (b + 1);
		rep(k, 1, b + 1) {
			rep(j, 1, x)cout << "R";
			if (y-- > 0)cout << "R";
			if (k != b + 1)cout << "B";
		}
		cout << endl;

k-LCM (easy version) - 洛谷 5

情况全列在样例了。可以讨论,如果n为偶数,有两种情况:

①n/2还是偶数,那这样就很简单了将n分为n/2,n/4,n/4刚好符合要求

②n/2为奇数,也很简单,n/2站一位,剩下两个位置,一个留给1不改变最小公倍数,剩下的(n/2)-1刚好站一位。

如果n为奇数,那么1站一位留下偶数(n-1)站两位,因为是偶数,所以剩下两位就为(n-1)/2。

 

	    if (n % 2 == 1) {
			cout << 1 << " ";
			n -= 1;
			cout << n / 2 << " " << n / 2 << endl;
		}
		else {
			if ((n / 2) % 2 == 0) {
				pln(n / 2, n / 4, n / 4);
			}
			else {
				pln(2, (n / 2) - 1, (n / 2) - 1);
			}
		}

CF1497C2 k-LCM (hard version) 6

这题跟上一题的区别就是k可以是任意大于3正整数。

但是我们也可以转换为上面那一题。不管有几个位置,我都只留下3个,其他都用1填充不影响最小公倍数。此时n的值变为(n-(k-3)),剩下3个位置,又变成了上面那一题

        cin >> n >> k;
		n -= (k - 3);

		rep(_, 1, k - 3)
			cout << 1 << " ";
		if (n % 2 == 1) {
			cout << 1 << " ";
			n -= 1;
			cout << n / 2 << " " << n / 2 << endl;
		}
		else {
			if ((n / 2) % 2 == 0) {
				pln(n / 2, n / 4, n / 4);
			}
			else {
				pln(2, (n / 2) - 1, (n / 2) - 1);
			}
		}

CF1693A Directional Increase 7

可以知道不管怎么移动,只要来回,区间的总和就不变恒等于0.首先后导0可以看作没去过,不考虑。因为整个移动区间的和都为0,且向右的且没有到终点再回来的地方是+1的操作更多

所以除了终点处等于0,其他情况前缀和始终大于0

        cin >> n;
		vector<int>arr;
		inln(arr, n);
		int ok = 0;
		int ans = 0;
		while (n && !arr[n])n--;//去除后导0,其实不必要
		for (int j = 1; j<=n; j--) {
			arr[j] += arr[j - 1];//前缀和
		}
		if (arr[n] != 0) {
			pln("No");
			continue;
		}
		rep(j, 1, n-1) {
			if (arr[j] <= 0) {
				pln("No");
				ok = 1;
				break;
			}
		}
		if (!ok)
			pln("Yes");

CF1694B Paranoid String 8

 

首先由题目很容易看出,每次操作也就是相当于把不同的两个字符删去左边那个留下右边那一个。

枚举以s[j]字符为结尾的子串,

如果s[j]==s[j-1]

也就是说这两个字符无法相消,那么以s[j]为结尾的前部分子串更无法消去此时答案数+1,因为本身s[j]这个字符就可以看作长度为1的字符串。

如果s[j]!=s[j-1]

可以证明,此时以s[j]为结尾的所有子串都会被消去只剩s[j]

设一个串xxxxx?01,首先我们知道题目的意思就是两个字符不同时保留右边那个。 

        如果当 " ?" 不同于0 .那么 "?" 在0的左边,且不相同,则可以被0消去。剩下字符串 xxxxx01  

         如果 "?" 和0相同,也就是说 "?" 和1不同。那么1可以先消去0,原串变成xxxxx?1,因为此时?和1不同,也可以看做xxxxx01。

所以不管哪种情况都能变成结尾为s[j-1]+s[j]的情况,接下来继续这样讨论直到消去j前面的所有字符。

所以子串s[j-j],s[j-1 - j],s[j-2 - j]...s[1-j]都可以被消去,此时答案+j,因为以s[j]结尾的子串 有j个。

代码很简单,但是我认为思维难度还是不小的....
 

cin >> s;
int ans = 0;
rep(j, 0, n - 1) {
	if (s[j] == s[j - 1])
		ans++;
	else if (s[j] != s[j - 1])
		ans += (j + 1);
}
pln(ans);

CF1681C Double Sort 9

 CF1692F 3SUM 10

可以先对每个元素%10,因为我们只需要个位,各位和%10的结果为3也就是原数相加结尾为3的情况。

接下来相当于是在一个数列中找出三个和的模为3的三个不同数。因为数据范围被限制到了0-9所以可以暴力枚举。

        inln(n);
		vector<int>arr(20);
		rep(j, 1, n) {
			cin >> k;
			arr[k % 10]++;
		}
		rep(j, 0, 9) {
			rep(i, 0, 9) {
				rep(k, 0, 9) {
					arr[j]--; arr[i]--; arr[k]--;
					if (!(arr[j] < 0 || arr[i] < 0 || arr[k] < 0)) 
					 {
						int t = (j + i + k) % 10;
						if (t == 3) {
							pln("YES");
							goto label;//跳出三重循环
						}
					}
					arr[j]++; arr[i]++; arr[k]++;
				}
			}
		}
		pln("No");
	label:;

 CF1550A Find The Array 11

 由贪心,最好的情况是元素都尽量大,个数才会小。所以构造公差为2的等比数列,找出第一个和大于S的个数,最后以为是减少使和调整为S。可以知道最后一位数不断减少,都会满足条件。

        cin >> n;
		int tem = 0;
		rep(j, 1, 100) {
			tem = j + (j * (j - 1));
			if (tem >= n) {
				pln(j);
				break;
			}
		}

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值