目录
Codeforces Round #783 (Div. 2)
Codeforces Round #784 (Div. 4)
Codeforces Round #783 (Div. 2)
A
题意:给定一个长宽分别为n,m的网格,起点为(1,1)每次可以上下左右移动,但这一次的移动不能与上一次的移动相同。问到右下角(n,m)的最小操作数。
思路:(图是借的qwq)
不能重复移动且步数最小的方法当然是沿着对角线右下移动,可以看到,移动到底边的时候就不能这样移动了,但是此时与终点同一水平线上。从图上可以看到,从左上右下移动的时候横坐标移动到min(n,m),纵坐标移动到min(n,m)所以如果当n==m且n和m没有等于1的,因为m和n中有一个为1说明移动只有一个方向,且另一个不为1则至少移动两次,产生矛盾。
所以总体思路就是先按右下移动到底边的(min(n,m),min(n,m))点处,然后剩下的步数则与剩下位移奇偶有关,剩下距离为奇的时候步数为2*(n-m)-1,反之为2*(n-m)。最后讨论一下特殊情况。
#include <iostream>
#include<ctime>
#include<math.h>
#include<string>
#include<vector>
#include<queue>
#include<map>
#include<set>
#include<cstring>
#include<algorithm>
#include<limits.h>
#include<unordered_map>
#include<unordered_set>
#define ll long long
const int N = 2e5 +10;
using namespace std;
int T,n,m;
ll arr[N];
int slove() {
cin >> n >> m;
int sum = 0;
if (n < m)swap(n, m);
if (m == 1) {
if (n >2)
return -1;
else
return n - 1;
}
else {
sum = 0;
sum = 2 * (m - 1);
if ((n - m) % 2 == 1) {
sum +=( 2 * (n - m))-1;
}
else
sum += (2 * (n - m));
}
return sum;
}
int main()
{
ios::sync_with_stdio(false);cout.tie(NULL);
cin >> T;
while (T--)
cout << slove()<< endl;
}
B
题意:
有 n个人围坐在圆桌旁,圆桌有m把椅子。
每个人希望其左侧、右侧分别至少有 a[i]个空椅子。判断所有人是否都能有座位。
思路:每个人的隐私距离是可以重叠的,由贪心,隐私距离重叠的越多,圆桌越有可能坐下所有人。所以考虑尽量多重叠隐私距离。但是后一个人的隐私距离又不能超过前一个人的隐私距离,不然前一个人不符合要求。所以考虑先对隐私距离排序,从最大开始安排座位就能保证前一个人不会和后一个人矛盾。我们先安排隐私距离最大人a[n]先坐,后一个人a[n-1]紧挨着前一个人的隐私距离此时所占的位置数为(2*a[n]+1)+a[n-1]+1,可以看到每次安排新人的时候位置数都增加了a[i]+1,
现在考虑最后一个人,最后一个人因为隐私距离最小,不用考虑重叠问题,只需要此时剩下至少一个位置即可。
#include <iostream>
#include<Windows.h>
#include<map>
#include<string>
#include<algorithm>
using namespace std;
int T,n,m;
const int N=2e5 +10;
int main()
{
cin>>T;
while(T--){
cin>>n>>m;
int arr[N];
for(int j=1;j<=n;j++)cin>>arr[j];
sort(arr+1,arr+1+n);
int sum=arr[n];//初始化
int ok=0;
for(int j=n;j>=2;j--){
sum+=arr[j]+1;每次增加占位数
if(sum>=m){
ok=1;
break;
}
}
if(ok){
cout<<"NO"<<endl;
}
else
cout<<"YES"<<endl;
}
return 0;
}
C
题意:有一个长为n的数组 a[n]和 元素全为0的b[n]。b[i]每次可以加上或减去a[i],问最少多少次操作可以使b[n]为严格递增序列。
思路:最少步骤的严格递增序列,且初始化为0,必定存在一个位置a[i]为0
以a[i] 为分界线,左侧的绝对值和右侧都是严格递增的,也就是左侧的靠左的绝对值|a[i-1]|一定比相邻靠右的绝对值|a[i+1]|至少大1,右侧靠右的a[i+1]至少比a[i]大1。所以按此逻辑枚举0分界点,取最小值即可。
#include <iostream>
#include<ctime>
#include<math.h>
#include<string>
#include<vector>
#include<queue>
#include<map>
#include<set>
#include<cstring>
#include<algorithm>
#include<limits.h>
#include<unordered_map>
#include<unordered_set>
#define ll long long
const int N = 2e5 +10;
using namespace std;
int mp[4][4];
int T,n;
ll arr[N];
int main()
{
ios::sync_with_stdio(false); cout.tie(NULL);
cin >> n;
for (int j = 1; j <= n; j++) {
cin >> arr[j];
}
ll ans = LLONG_MAX;
for (int j = 1; j <= n; j++) {
ll now = 0, sum = 0;
for (int i = j - 1; i >= 1; i--) {
ll t=(ceil((double)(now+1)/arr[i]));//前一个至少比当前少一
sum += t;
now = t * arr[i];
}
now = 0;//换界记录值初始化
for (int i = j + 1; i <= n; i++) {
ll t = ceil((double)(now + 1) / arr[i]);
sum += t;
now = t * arr[i];
}
ans = min(ans, sum);
}
cout << ans << endl;
}
Codeforces Round #784 (Div. 4)
A
A太水了懒得写。
B
题意:输出出现至少三次的数,没有返回0;
思路:桶/hash计数,没啥好说的。
#include <iostream>
#include<ctime>
#include<math.h>
#include<string>
#include<vector>
#include<queue>
#include<map>
#include<set>
#include<cstring>
#include<algorithm>
#include<limits.h>
#include<unordered_map>
#include<unordered_set>
#define ll long long
using namespace std;
int T, n;
int main()
{
/* freopen("in.txt", "r", stdin);
freopen("out.txt", "w", stdout);*/
ios::sync_with_stdio(false); cout.tie(NULL);
cin >> T;
while (T--) {
int ok = 0;
int arr[200001] = { 0 }, b[200001] = { 0 };
cin >> n;
for (int j = 1; j <= n; j++) {
cin >> arr[j];
b[arr[j]]++;
}
for (int j = 0; j <= n; j++) {
if (b[j] >= 3) {
cout << j << endl;
ok = 1;
break;
}
}
if(!ok)
cout << -1 << endl;
}
}
C
题意:给定一个序列a[n],每次可以给下标为奇数i的元素a[i]全部加上1也可以给下标为偶数j的元素a[j]全加上1,问多次或0次操作后a序列能否使a序列全部元素都相等。
思路:很简单,如果相邻的奇数或偶数元素存在不相等的情况,那么如何操作都不能使这不相等的两个元素相同,因为他们都是共同加1的。
#include <iostream>
#include<ctime>
#include<math.h>
#include<string>
#include<vector>
#include<queue>
#include<map>
#include<set>
#include<cstring>
#include<algorithm>
#include<limits.h>
#include<unordered_map>
#include<unordered_set>
#define ll long long
using namespace std;
int T, n;
int main()
{
/* freopen("in.txt", "r", stdin);
freopen("out.txt", "w", stdout);*/
ios::sync_with_stdio(false); cout.tie(NULL);
cin >> T;
while (T--) {
int n;
int arr[100] = { 0 };
cin >> n;
for (int j = 1; j <= n; j++) {
int t;
cin >> t;
if (t % 2 == 0)
arr[j] = 0;
else
arr[j] = 1;
}
int ok = 0;
for (int j = 3; j <= n; j++) {
if (arr[j] != arr[j - 2]) {
ok = 1;
break;
}
}
if (ok)
cout << "NO" << endl;
else cout << "YES" << endl;
}
}
D
题意:
给定长度为n的方格,方格默认为白色W,可以用长度为2的一半红R一半蓝B的邮票贴入,可以RB的贴也可以BR的贴,后来的颜色会覆盖原来的颜色,现给定一个长度为n的方格,问这串方格是否是合法的。
思路:
这题算是个结论题,可以先找找规律。显然当两个W之间存在连续的R或者B肯定是不合法的。所以去统计两个W之间的R和B的个数,如果只有其中一个为0,说明不合法。对于第一个颜色和最后一个颜色,我们可以在头尾加上W使其一般化。
#include <iostream>
#include<ctime>
#include<math.h>
#include<string>
#include<vector>
#include<queue>
#include<map>
#include<set>
#include<cstring>
#include<algorithm>
#include<limits.h>
#include<unordered_map>
#include<unordered_set>
#define ll long long
using namespace std;
int T, n;
int main()
{
/* freopen("in.txt", "r", stdin);
freopen("out.txt", "w", stdout);*/
ios::sync_with_stdio(false); cout.tie(NULL);
cin >> T;
while (T--) {
string s;
cin >> n;
cin >> s;
s += 'W';
int ok = 0;
int r = 0, b = 0, cnt = 0;
for (int j = 0; j <= n; j++) {
if (s[j] == 'W') {
if ((r == 0 || b == 0) && cnt != 0) {
ok = 1;
break;
}
else
r = 0, b = 0, cnt = 0;//重置计算下一段
}
else {
if (s[j] == 'R')r++, cnt++;
if (s[j] == 'B')b++, cnt++;
}
}
if (!ok)
cout << "YES" << endl;
else
cout << "NO" << endl;
}
}
E
题意:
每次给出n个长度为2且由a-k的字母组成的字符串,问存在多少对只有其中一个对应下标的字符不同的字符串对数。
思路:
这题可以从字符串长度只有2入手,如果其中一个字符和另一个字符串的相应位置字符相同,,那么要满足题目要求,另一个位置上的字符则必须不同于另一个字符串相应位置的字符。
对于这种情况如果直接逐个比对必会超时,所以采用hash计数,第一个hash1得到第一个位置上该字符出现的次数,hash2得到第二个位置上该字符串出现次数,hash3处理这两个字符串完全相等的情况,其值为整个字符串出现次数。所以 其中一个字符相同的情况 减去 两个字符都相等的情况则能得到当前字符对应相同但另一个位置的字符对应不同的情况。那么显然对于前 i 个字符串,符合题目要求的字符串对数sum=(hash1-hash3)+(hash2-hash3)
#include <iostream>
#include<ctime>
#include<math.h>
#include<string>
#include<vector>
#include<queue>
#include<map>
#include<set>
#include<cstring>
#include<algorithm>
#include<limits.h>
#include<unordered_map>
#include<unordered_set>
#define ll long long
using namespace std;
int T, n;
int main()
{
/* freopen("in.txt", "r", stdin);
freopen("out.txt", "w", stdout);*/
ios::sync_with_stdio(false); cout.tie(NULL);
cin >> T;
while (T--) {
ll N, n = 0;
cin >> N;
map<char, int>m1, m2;
map<string, int>m3;
while (N--) {
string s; cin >> s;
n += m1[s[0]] + m2[s[1]] - 2 * m3[s];
m3[s]++; m1[s[0]]++; m2[s[1]]++;
}
cout << n << endl;
}
}
F
题意:
有一列重量不一的糖果,一个人从左边连续的吃,另一个人从右边连续的吃。输出能使两人吃的糖果总重量相同的被吃糖果总数。
思路:
其实题目很简单,也没有卡时间,我的原始思路是用两个前缀和处理两个人分别吃到某个糖果已经吃的总重量。然后枚举其中一个人吃的糖果总数,再二分查找另一个人可以吃的糖果总数是否有与第一个人当前吃的相等的,并且不断更新糖果总数最大值。
#include <iostream>
#include<ctime>
#include<math.h>
#include<string>
#include<vector>
#include<queue>
#include<map>
#include<set>
#include<cstring>
#include<algorithm>
#include<limits.h>
#include<unordered_map>
#include<unordered_set>
#define ll long long
using namespace std;
int T, n;
ll sum1[200001] = { 0 }, sum2[200001] = { 0 };
ll my_find(ll t) {//二分查找
ll l = 0, r = n + 1;
ll mid;
while (l + 1 != r) {
mid = (l + r) >> 1;
if (sum2[mid] > t) {
l = mid;
}
else
r = mid;
}
return r;
}
int main()
{
ios::sync_with_stdio(false); cout.tie(NULL);
cin >> T;
while (T--) {
cin >> n;
ll ans = 0;
for (int j = 1; j <= n; ++j) {
ll t;
cin >> t;
sum1[j] = sum1[j - 1] + t;
}
for (int j = 1; j <= n; ++j) {
sum2[j] = sum1[n] - sum1[j];//后缀和处理
}
for (int j = 1; j <= n; ++j) {
if (sum1[j] > (sum1[n] / 2)){//过半剩下的不可能相等,舍去
break;
}
ll t = my_find(sum1[j]);//二分查找后缀和相等处
if (t < n && sum2[t] == sum1[j]) {
ans = j + (n - t);
}
}
cout << ans << endl;
}
}
整体时间复杂度为O(n*log)但是因为处理前后缀常数会稍大。
还有个做法,很明显一个从左吃一个从右吃要使总和相等,就是一个双指针问题,用两个指针记录双方各吃到的位置,再分别用两个变量记录吃到当前位置的总重量,然后根据当前情况调整指针即可。
#include <iostream>
#include<ctime>
#include<math.h>
#include<string>
#include<vector>
#include<queue>
#include<map>
#include<set>
#include<cstring>
#include<algorithm>
#include<limits.h>
#include<unordered_map>
#include<unordered_set>
#define ll long long
const int N = 2e5;
using namespace std;
int T, n;
int w[N+10];
void slove() {
for (int j = 1; j <= n; ++j)cin >> w[j];
ll ans = 0, l = 0, r = n + (ll)1;
ll a = 0, b = 0;
while (l + 1 <= r) {
if (a == b) {//相等时更新答案的最大值
ans = l + (n - r) + 1;
a += w[++l]; b += w[--r];//指针全部偏移
}
else {//两边重量不等时调整指针
if (a > b) {
b += w[--r];
}
else
a += w[++l];
}
}
cout << ans << endl;
}
int main()
{
ios::sync_with_stdio(false); cout.tie(NULL);
cin >> T;
while (T--) {
cin >> n;
slove();
}
}