[Codeforces Round #617 (Div. 3)] 题解 A,B,C,D,E1,E2,F
1296A - Array with Odd Sum
思路:
如果一开始数组的sum和是奇数,那么直接YES,
否则:如果存在一个奇数和一个偶数,答案为YES,否则为NO
代码:
int n;
int a[maxn];
int main()
{
//freopen("D:\\code\\text\\input.txt","r",stdin);
//freopen("D:\\code\\text\\output.txt","w",stdout);
int t;
t = readint();
while (t--)
{
n = readint();
int sum = 0;
repd(i, 1, n) {
a[i] = readint();
sum += a[i];
}
if (sum & 1)
{
printf("YES\n");
} else
{
int isok1 = 0;
int isok2 = 0;
repd(i, 1, n)
{
if (a[i] & 1)
{
isok1 = 1;
} else
{
isok2 = 1;
}
}
if (isok2 == 1 & isok1 == 1)
{
printf("YES\n");
} else
{
printf("NO\n");
}
}
}
return 0;
}
B. Food Buying
思路:
直接贪心思想模拟即可,
如果 s>9,那么花掉 \(s-s mod 10\) 的钱,还剩下 s%10+s/10 ,继续迭代。
否则 直接花掉所有钱,剩下0元,结束。
Time complexity: \(O(log_{10}(s))\)per test case.
代码:
int main()
{
//freopen("D:\\code\\text\\input.txt","r",stdin);
//freopen("D:\\code\\text\\output.txt","w",stdout);
int t;
t = readint();
int s;
int ans;
while (t--)
{
s = readint();
ans = 0;
while (s)
{
if (s > 9)
{
ans += s / 10 * 10;
s = (s % 10) + (s / 10);
} else
{
ans += s;
s = 0;
}
}
printf("%d\n", ans );
}
return 0;
}
C. Yet Another Walking Robot
思路:
题目让删除一个长度最小的子串使机器人最后的位置不变,即如果机器人单独执行删除的子串的话,首尾位置不变。
我们设机器人的在接到第i个指令后的位置是\((xi,yi)\),用map查找下之前有没有出现过该位置。
如果有,令j = map[\((xi,yi)\)] ,那么 \([j+1,i]\) 这段子串就符合条件,维护出长度最小的符合条件的子串即可。同时用map记录(或者是更新)下位置\((xi,yi)\) -> i
map是STL中的一个容器,std::map for C++
我的代码中 map存的\((xi,yi)\) 对应的是i+1,这样在维护答案时更方便。
Time complexity: \(O(nlogn)\) per test case.
代码:
int n;
char s[maxn];
map<pii, int> m;
int main()
{
//freopen("D:\\code\\text\\input.txt","r",stdin);
//freopen("D:\\code\\text\\output.txt","w",stdout);
int t;
t = readint();
while (t--)
{
n = readint();
m.clear();
scanf("%s", s + 1);
int x = 0;
int y = 0;
int len = inf;
int l, r;
m[mp(0, 0)] = 1;
repd(i, 1, n)
{
if (s[i] == 'L')
{
x--;
} else if (s[i] == 'R')
{
x++;
} else if (s[i] == 'U')
{
y++;
} else
{
y--;
}
if (m.count(mp(x, y)))
{
int j = m[mp(x, y)];
if (i - j + 1 < len)
{
len = i - j + 1;
l = j;
r = i;
}
}
m[mp(x, y)] = i+1;
}
if (len != inf)
printf("%d %d\n", l, r);
else {
printf("-1\n");
}
}
return 0;
}
D. Fight with Monsters
思路:
很明显的,我们可以将每一个h[i]模去(a+b),得到新的h[i],如果它变成了零,让“回滚”的一个轮回。
然后对于每一个怪兽,我们需要耗费\(\lceil\frac{h_i}{a}\rceil - 1\) 次魔法。
将\(h[i] =\lceil\frac{h_i}{a}\rceil - 1\) 后,从小到大排个序后贪心取一下即可。
Time complexity: \(O(nlogn)\)
代码:
int n;
ll a, b, k;
ll h[maxn];
ll c;
std::vector<ll> v;
int main()
{
//freopen("D:\\code\\text\\input.txt","r",stdin);
//freopen("D:\\code\\text\\output.txt","w",stdout);
n = readint();
a = readll(); b = readll(); k = readll();
c = a + b;
repd(i, 1, n)
{
h[i] = readll();
}
int ans = 0;
repd(i, 1, n)
{
h[i] %= c;
if (h[i] == 0)
h[i] += c;
if (h[i] <= a)
{
ans++;
} else
{
ll t = h[i] / a;
if (h[i] % a != 0)
{
t++;
}
v.push_back(t - 1);
}
}
sort(ALL(v));
for (auto x : v)
{
if (x <= k)
{
ans++;
k -= x;
}
}
printf("%d\n", ans );
return 0;
}
E1. String Coloring (easy version) /1296E2 - String Coloring (hard version)
思路:
解决本问题应该清楚 狄尔沃斯定理
百科的解释为:
狄尔沃斯定理(Dilworth's theorem)亦称偏序集分解定理,是关于偏序集的极大极小的定理,该定理断言:对于任意有限偏序集,其最大反链中元素的数目必等于最小链划分中链的数目。此定理的对偶形式亦真,它断言:对于任意有限偏序集,其最长链中元素的数目必等于其最小反链划分中反链的数目,由偏序集P按如下方式产生的图G称为偏序集的可比图:G的节点集由P的元素组成,而e为G中的边,仅当e的两端点在P中是可比较的,有限全序集的可比图为完全图
This theorem says that the minimum number of non-decreasing sequences we need to cover the whole sequence equals the length of least decreasing subsequence.
通过分析可发现,
我们从字符串的末尾向左求最长不下降子序列,
用f[i] 代表 第i个位置在最长不下降子序列中的位置。
那么 总体就需要 $max(f[i],i\in[1,n]) $ 个颜色才能满足要求。
第i个字符就染f[i] 色即可。
E1代码:
int n;
char s[maxn];
int ans[maxn];
std::vector<char> v;
int isok = 1;
int main()
{
//freopen("D:\\code\\text\\input.txt","r",stdin);
//freopen("D:\\code\\text\\output.txt","w",stdout);
n = readint();
scanf("%s", s + 1);
ans[n] = 0;
v.push_back(s[n]);
for (int i = n - 1; i >= 1; --i)
{
int pos = lower_bound(ALL(v), s[i]) - v.begin();
ans[i] = pos;
if(pos==sz(v))
{
v.push_back(s[i]);
}else
v[pos] = s[i];
if (pos > 1)
{
isok = 0;
}
}
if (isok)
{
printf("YES\n");
repd(i, 1, n)
{
printf("%d", ans[i] );
}
printf("\n");
} else
{
printf("NO\n");
}
return 0;
}
E2代码:
int n;
char s[maxn];
int ans[maxn];
std::vector<char> v;
int isok = 1;
int main()
{
//freopen("D:\\code\\text\\input.txt","r",stdin);
//freopen("D:\\code\\text\\output.txt","w",stdout);
n = readint();
scanf("%s", s + 1);
ans[n] = 1;
v.push_back(s[n]);
int ansnum = 1;
for (int i = n - 1; i >= 1; --i)
{
int pos = lower_bound(ALL(v), s[i]) - v.begin();
ans[i] = pos + 1;
if (pos == sz(v))
{
v.push_back(s[i]);
} else
v[pos] = s[i];
ansnum = max(ansnum, ans[i]);
}
printf("%d\n", ansnum);
repd(i, 1, n)
{
printf("%d%c", ans[i], i == n ? '\n' : ' ' );
}
return 0;
}
F. Berland Beauty
思路:
首先把给定的m个限制按照降序排序,
然后降序的对于每一个限制\((ai,bi,gi)\),我们通过dfs 得出 数组\(fa[i]\) 代表 在ai到 bi 的有向路径中 到达节点i的前继节点。然后利用数组fa,更新ai到 bi 路径中边j的边值 \(f_j = max(f_j, g_i)\) ,并用bj 标记下路径中是否有边j ,满足 fj<gi 。如果没有则答案是-1,最后如果各个限制没有冲突,将那些没有更新到的边权设一个[1,1e6] 中的任意值。
代码:
int n;
int id[maxn][maxn];
int fa[maxn];
struct node
{
int f, t, w;
bool operator < (const node &b) const
{
return w > b.w;
}
} e[maxn];
int ans[maxn];
int m;
std::vector<int> son[maxn];
void dfs(int x, int pre)
{
for (auto y : son[x])
{
if (y != pre)
{
fa[y] = x;
dfs(y, x);
}
}
}
int main()
{
//freopen("D:\\code\\text\\input.txt","r",stdin);
//freopen("D:\\code\\text\\output.txt","w",stdout);
n = readint();
repd(i, 2, n)
{
int x, y;
x = readint();
y = readint();
son[x].push_back(y);
son[y].push_back(x);
id[x][y] = id[y][x] = i;
}
m = readint();
repd(i, 1, m)
{
e[i].f = readint();
e[i].t = readint();
e[i].w = readint();
}
sort(e + 1, e + 1 + m);
int flag = 1;
repd(i, 1, m)
{
dfs(e[i].f, 0);
int now = e[i].t;
int bj = 0;
do
{
int j = id[now][fa[now]];
if (ans[j] <= e[i].w)
{
bj = 1;
ans[j] = e[i].w;
}
now = fa[now];
} while (now != e[i].f);
if (!bj)
{
flag = 0;
break;
}
}
if (flag)
{
repd(i, 2, n)
{
printf("%d%c", max(ans[i], 1), i == n ? '\n' : ' ');
}
} else
{
printf("-1\n");
}
return 0;
}