Codeforces Round #847 (Div. 3) ABCDE

阿里云国内75折 回扣 微信号:monov8
阿里云国际,腾讯云国际,低至75折。AWS 93折 免费开户实名账号 代冲值 优惠多多 微信号:monov8 飞机:@monov6

url:Dashboard - Codeforces Round #847 (Div. 3) - Codeforces

A. Polycarp and the Day of Pi

题意:

判断给的字符串前多少位跟PI一样

思路:

打个表,然后遍历一下即可,遇到不是的就退出

代码:

string op = "314159265358979323846264338327950288419716939937510";
 
void solve()
{
	string s1;
	cin >> s1;
	for(int i = 0;i < s1.size();i++)
	{
		if(s1[i] != op[i])
		{
			cout << i << endl;
			return;
		}
	}
	cout << s1.size() << endl;
}

总结:

水题,不总结(

B. Taisia and Dice

题意:

给n个骰子,和n个骰子的点数之和和去掉最大点数的点数之和

思路:

先可以求出最大的骰子点数maxn

然后用剩下的点数去分配给n - 1个骰子,使得点数为[1,maxn]

最开始想的是弄个平均值啥的

后来一看范围就直接暴力好了,就循环一个一个放,这样保证了一定平均值是最小的

但是今天写题解时想了想,居然把昨天那种平均值方法写出来了

就是先求出$x = res / (n - 1)$和$y = res % (n - 1)$

然后输出前$n - 1 - y$个$x$

再输出前$y$个$x + 1$

最后输出$maxn$

代码1:

void solve()
{
	int sum,res;
	cin >> n >> sum >> res;
	vector<int> a(n + 1);
	int maxn = sum - res;
	for(int i = 1;i <= n - 1;i++)
	{
		if(res > 0) a[i]++;
		else break;
		res--;
		if(i == n - 1) i = 0;
	}
	for(int i = 1;i <= n - 1;i++) cout << a[i] << " ";
	cout << maxn << endl;
}

代码2:

void solve()
{
	int sum,res;
	cin >> n >> sum >> res;
	vector<int> a(n + 1);
	int maxn = sum - res;
	int x = res / (n - 1);
	int y = res % (n - 1);
	// cout << x << " " << y << endl;
	for(int i = 1;i <= n - 1 - y;i++) cout << x << " ";
	for(int i = 1;i <= y;i++) cout << x + 1 << " ";
	cout << maxn << endl;
}

C. Premutation

题意:

给n个n - 1长度的排列

每个排列都为一个原始排列去掉了一个数所成的,保证n个排列去掉的数不相同

思路:

主要是读题就读了很久,大概知道什么意思后感觉就看出来规律

主要是顺序是乱的,所以看起来很难受

后来想一想,

每个位置都少出现一次

那只要看第一个位置,必然会有一个不同的

找到不同的那个位置,然后输出其余n - 1个相同的那个数

再输出剩下的n - 1个位置的数即可

但是这种过程中想用用全部异或以来那个技巧

然后才发现那个是用来找所有数都出现偶数次,只有一个数出现奇数次才能用的

这个时候用map老老实实暴力就好了

别想着花里胡哨的(

代码:

void solve()
{
	cin >> n;
	for(int i = 1;i <= n;i++)
		for(int j = 1;j <= n - 1;j++)
			cin >> a[i][j];
	map<int,int> mp;
	for(int i = 1;i <= n;i++) mp[a[i][1]]++;
	int q1,q2,idx;
	for(auto it : mp)
	{
		if(it.se == 1) q1 = it.fi;
		else q2 = it.fi;
	}
	for(int i = 1;i <= n;i++)
	{
		if(a[i][1] == q1)
		{
			idx = i;
			break;
		}
	}
	cout << q2 << " ";
	for(int i = 1;i <= n - 1;i++) cout << a[idx][i] << " ";
	cout << endl;
}

总结:

别想着玩花的技巧

就算多跑个循环,多开点变量,都不会有事(

能过就行(

D. Matryoshkas

题意:

给n个数,问最少能凑成几个集合

思路:

贪心凑,排序后从最小的开始,然后找这个数+1的数

直接双重循环 + 标记数组会TLE

这时候需要用map来优化一波

还是先排序,不过先把每个值装到map里

还是按照a[i]的1到n来遍历

不过每次先检查map[a[i]]是否为空

然后再将循环减去a[i]递增的值

这样就省去了开标记数组的时间和一部到位直接到下一个值

而不是一堆continue来占用时间

暴力代码:

void solve()
{
	int ans = 0;
	cin >> n;
	vector<int> a(n + 1),st(n + 1,0);
	for(int i = 1;i <= n;i++) cin >> a[i];
	sort(all(a));
	for(int i = 1;i <= n;i++)
	{
		int x = a[i] + 1;
		if(st[i]) continue;
		for(int j = i + 1;j <= n;j++)
		{
			if(st[j]) continue;
			if(a[j] == x)
			{
				st[j] = 1;
				x++;
			}
		}
		ans++;
	}
	cout << ans << endl;
}

优化代码:

void solve()
{
	int ans = 0;
	map<int,int> mp;
	cin >> n;
	vector<int> a(n + 1);
	for(int i = 1;i <= n;i++) cin >> a[i],mp[a[i]]++;
	sort(all(a));
	for(int i = 1;i <= n;i++)
	{
		if(mp[a[i]])
		{
			int idx = a[i];
			while(mp[idx])
			{
				mp[idx]--;
				idx++;
			}
			// cout << idx << endl;
			ans++;
		}
	}
	cout << ans << endl;
}

总结:

如果暴力过程中出现了一堆continue的情况

这时候就可以用map来进行优化

E. Vlad and a Pair of Numbers

题意:

给一个数n,要你求两个数a和b,使得$a 异或 b == n$而且$(a + b) / 2 == n$

思路:

考虑位运算

$(a + b) / 2$要求$a + b$肯定要是个偶数

所以a和b要么同为奇数,要么同为偶数

但是n为奇数的话二进制形式最后一位为1

显然矛盾了,所以n只能为偶数

然后看公式,就是要求两个数相加后向右移一位等于这两个数异或起来的值

首先构造$a 异或 b == n$

如果n的位置上为1,a,b则可以放1 0或者0 1

如果n的位置上为0,a,b则可以放1 1或者0 0

然后再构造$(a + b) / 2 == n$

其实就是二进制形式相加进位后我们希望结果为n向左移一位

这样再/2向右移一位,这样刚好结果为n

那就变成了如何安排a和b的二进制形式使得结果为n向左移一位

实际向左移一位就是所有1进位即可

要想要所有1进位,安排如下:
1变成1 0

1后面的一个0变成 1 1

其余0变成0 0

这样即可保证所有1进位

另外不能有连续的1出现在n中

因为连续的1的话,要么就留在那里不动,要么这几位都变成0

不能保证向左移一位

代码:

void solve()
{
	cin >> n;
	if(n&1)
	{
		cout << -1 << endl;
		return;
	}
	bitset<32> op(n),a(0),b(0);
	int x = 0,y = 0;
	bool flag = 0;
	for(int i = 31;i >= 0;i--)
	{
		if((i != 0)&&(op[i] == 1)&&op[i - 1] == 1)
		{
			cout << -1 << endl;
			return;
		}
		if(op[i] == 1)
		{
			a[i] = 1;
			b[i] = 0;
			flag = 1;
		}
		else if(flag)
		{
			a[i] = 1;
			b[i] = 1;
			flag = 0;
		}
		else
		{
			a[i] = 0;
			b[i] = 0;
		}
		if(a[i]) x += 1 << i;
		if(b[i]) y += 1 << i;
	}
	cout << x << " " << y << endl;
}

总结:

看到^和数据范围给的是2的次方的时候考虑位运算

从公式入手,*2代表左移,/2代表右移

奇数和偶数都举一个例子

 
阿里云国内75折 回扣 微信号:monov8
阿里云国际,腾讯云国际,低至75折。AWS 93折 免费开户实名账号 代冲值 优惠多多 微信号:monov8 飞机:@monov6