[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;
}
