Codeforces Round #788 (Div. 2) A~D
比赛题目链接
https://codeforces.com/contest/1670
My contest submissions
比赛时只搞定了三题,D题算是大爆种(我也就只能欺负欺负枚举找规律的题)
A.Prof. Slim(思维)
题意
给出一个序列,可以移动每个字符前的正负符号,问能否通过这种操作让序列变得升序.
分析
记录负数的个数cnt,然后把所有数字取绝对值,再从前往后添加cnt个负号,最后判断是否为升序
#include<bits/stdc++.h>
using namespace std;
typedef long long LL;
const int N = 1e5 + 5;
int a[N];
int main()
{
int T;
cin >> T;
while (T--)
{
int n;
cin >> n;
int cnt = 0;
for (int i = 1; i <= n; i++)
{
cin >> a[i];
cnt += (a[i] < 0);
}
for (int i = 1; i <= cnt; i++)
a[i] = -abs(a[i]);
for (int i = cnt + 1; i <= n; i++)
a[i] = abs(a[i]);
if (is_sorted(a + 1, a + n + 1))
{
cout << "YES" << endl;
}
else
{
cout << "NO" << endl;
}
}
}
B.Dorms War
题意
给出一个字符串,并给出一个字母集合,每次操作中如果字符串中某个位置其后面一个字母在给出的字母集合中,那么这个位置就会被删去,求操作最多能进行多少次.
分析
就是要求每两个相邻的特殊字符之间的最大距离
#include<bits/stdc++.h>
using namespace std;
typedef long long LL;
const int N = 1e5 + 5;
char s[N];
bool v[30];//记录特殊字母
int main()
{
int T;
cin >> T;
while (T--)
{
memset(v, 0, sizeof v);
int n, m;
cin >> n >> s >> m;
for (int i = 0; i < m; i++)
{
char c;
cin >> c;
v[c - 'a'] = true;
}
//从后往前扫一遍, 记录每个字母后方距离其最近的特殊字母位置即可
int res = 0, lastpos = 0;
for (int i = n - 1; i >= 0; i--)
{
if (lastpos)
res = max(res, lastpos - i);
if (v[s[i] - 'a'])
lastpos = i;
}
cout << res << '\n';
}
return 0;
}
D.Very Suspicious
题意
在一个无限大的由六边形的平面上用直线(直线必须与六边形的某条边平行)分割出等边三角形,问要分割出n个等边三角形至少需要多少条直线.
分析
结论很简单.首先平面上的直线只有三种(0度, 60度, 120度), 同一种类别的直线相互平行, 不可能有交点, 在最贪的情况下,每种直线和另外两种直线每一条都会有一个交点, 经过“简单的画图枚举”,总结出一个公式,画出上一根线后场上的三角形 + 这根线经过的所有线的个数 * 2 = 画出这根线后场上的等腰三角形
#include<bits/stdc++.h>
using namespace std;
typedef long long LL;
const int N = 1e5 + 5;
LL f[N];
int main()
{
//初始化最初的三根线能切出的三角形
f[1] = 0 ,f[2] = 2, f[3] = 6;
//记录三种线(0度,60度,120度)的个数
int cnt[] = { 1, 1, 1 };
//两眼一闭,直接从第四根线开始枚举(10^9只需要38730根)
for (int i = 4; i <= 38730; i++)
{
//鉴定当前是三根线的哪一种
int kind = i % 3;
//继承上一根线的结果
f[i] = f[i - 1];
//对三种线进行枚举
for (int j = 0; j < 3; j++)
{
//种类不同,意味着一定要相交
if (j != kind)
//上一根线的所得到的三角形 + 这根线经过的所有线的个数 * 2 = 这根线能造出来的等腰三角形
f[i] += 2 * cnt[j];
}
cnt[kind]++;
}
int T;
//cin改scanf,过test 5
scanf("%d", &T);
while (T--)
{
LL n;
cin >> n;
//for遍历寻找换成lower_bound过test 6以及所有样例
printf("%d\n", (lower_bound(f, f + 38730, n) - f));
}
return 0;
}
My Submissions
C.Where is the Pizza?
题意
有两个排列a和b,对于c的每个位置,令c[i] = a[i]或者c[i] = b[i],要求最终得到c[i]也是一个排列,其中c[i]中某些位置一开始就已经选好了,求能得到的合法排列(1~n全部出现,并且不能重复)c[i]的数目;
分析
这题是真的没想到要看成环来做,一开始用纯纯的dfs,不出所料地吃了个TLE;
首先假设i位置的c[i]是被选好的,那么a[i]和b[i]在i位置至少有一个要等于c[i],此处我们假设为a[i];同时,在某处,假设为j位置上,b[j]会等于a[i],那么这个b[j]是无法被选择的,只能选择a[j];
所以这样看来,部分位置的选法其实已经暗中注定,所以我们可以将a[i],b[i],a[j],b[j]连接成一个环,例如1,3,5,1;只要有一个点确定了,那么换上的选法就是确定的;当我们把环全部列举出来后,剩下的k个位置,那么就会有2^k个答案。
#include<bits/stdc++.h>
using namespace std;
typedef long long LL;
const int maxn = 1e5 + 5, mod = 1e9 + 7;
int a[maxn], b[maxn], c[maxn], p[maxn];
bool st[maxn], v[maxn];
int main()
{
int T;
cin >> T;
while (T--)
{
int n;
cin >> n;
memset(v, 0, sizeof v);
memset(st, 0, sizeof st);
for (int i = 1; i <= n; i++)
cin >> a[i];
for (int i = 1; i <= n; i++)
cin >> b[i];
for (int i = 1; i <= n; i++)
cin >> c[i];
for (int i = 1; i <= n; i++)
if (c[i])
st[a[i]] = st[b[i]] = 1;
for (int i = 1; i <= n; i++)
p[a[i]] = b[i];
LL res = 1;
for (int i = 1; i <= n; i++)
{
if (!v[i])
{
int cnt = 0;
bool flag = 0;
for (int j = i; !v[j]; j = p[j])
{
v[j] = 1;
cnt++;
flag |= st[j];
}
if (cnt != 1 && !flag)
res = res * 2 % mod;
}
}
cout << res << '\n';
}
}

浙公网安备 33010602011771号