本来以为会掉分,结果竟然涨了
A - In-game Chat
签到题
给定只含')'和小写字母的字符串,问结尾的')'数量是否比剩余的字符数量多
签到题,直接从后往前扫就行
B - Fair Numbers
思维
求大于等于(n)的能被每一位数整除的最小的数
如果一个数能被(a,b)整除,那么他一定也能被(lcm(a,b))整除
(1,2,...,9)的最小公倍数是(2520),最小的公平数一定小于等于(1,2,...,9)都出现的公平数,所以公平数和(n)直接差距很小,直接枚举即可
这题浪费了比较长时间,一开始想了个假贪心,后来忽略除数为(0)的情况了
while (x) {
if (x % 10 == 0) { x /= 10; continue; }//!!!
if (y % (x % 10)) return 0;
x /= 10;
}
C - Peaceful Rooks
思维+图论
给定(n*n)的网格,有(m)个马((m < n)),每个马能垂直或者水平移动任意距离,马之间不能互相攻击
问最少多少步能把所有马都移动到主对角线((i,i))
比赛时只想如果相互攻击,就可以想把一个移动到空闲位置,然后剩下的那个移动到对角线,空闲位置的再移动,但是并没有想到怎么实现
我们不妨建立图看一下,位置(x,y)对应图中边(x,y),根据题目的性质,每个点最多只有一条出边
如果出现了环,就说明这几个能互相攻击,我们都需要多一步去把其中一个先移动到空闲位置,也就是说环的步数比正常路径多了(1)
所以本题只需要建图找环就行了。
参考代码,用的并查集。
int T, n, m;
int f[N];
int find(int x){
if(f[x] == x) return x;
else return f[x] = find(f[x]);
}
int main()
{
T = read();
while(T--){
int ans = 0;
n = read(), m = read();
for(int i = 1; i <= n; ++i) f[i] = i;
for(int i = 1; i <= m; ++i){
int x = read(), y = read();
if(x == y) continue;
ans++;
int fx = find(x), fy = find(y);
if(fx == fy) ans++;//环
else f[fx] = fy;
}
cout << ans << '
';
}
return 0;
}
本题提供了一中二维坐标系建图的思路
以前我只遇到过一次这种二维坐标系建图的题目,这一次完全没想到,要长点记性
D - Grime Zoo
思维+贪心
给你一个由(‘0’) 和(‘1’)和 (‘?‘)组成,其中有一个(01)子序列就会产生(x)点愤怒值。每有一个(10)子序列就会产生(y)点愤怒值。其中问号(’?')可以变成(0)或者是(1)。问愤怒值的和最小为多少。
这题给我的第一感觉是(dp),可惜我只想到一个空间复杂度是(n^2)的(dp),数组开不下,竟然没想到贪心.....
接下来就说一下贪心的思路吧,这题应该有很多思路,不过我就看懂了贪心(最好想)
显然(x)和(y)哪个大,我们就尽量要哪个多,详细地来说就是:
如果(x>y),我们就让(10)尽量多,就把(1)尽量往前填。
如果(x<y),我们就让(01)尽量多,就把(1)尽量往后填。
具体实现是可以通过前缀和和后缀和加速。
代码参考网上的题解(我不会写
const int N = 1e5 + 2020;
char str[N];
int x, y, st[N][3], ed[N][3];
vector<int> g;
signed main()
{
scanf("%s", str + 1);
scanf("%lld%lld", &x, &y);
int len = strlen(str + 1);
for(int i = 1; i <= len; ++i){
st[i][0] = st[i - 1][0];
st[i][1] = st[i - 1][1];
st[i][2] = st[i - 1][2];
if(str[i] == '0') st[i][0]++;
else if(str[i] == '1') st[i][1]++;
else{
st[i][2]++;
g.push_back(i);
}
}
for(int i = len; i >= 1; --i){
ed[i][0] = ed[i + 1][0];
ed[i][1] = ed[i + 1][1];
ed[i][2] = ed[i + 1][2];
if(str[i] == '0') ed[i][0]++;
else if(str[i] == '1') ed[i][1]++;
else ed[i][2]++;
}
int num0 = 0, num1 = 0;
int ans = 0;
for(int i = 1; i <= len; ++i){
if(str[i] == '0'){
ans += num1 * y;
num0++;
}
else {//此处默认?是1,注意后面把?修改成0时要先减去此时多算的部分
ans += num0 * x;
num1++;
}
}
int res = ans;
if(x < y) //10更大
{
for(int i = 0; i < g.size(); ++i){
int pos = g[i];
res = res - (st[pos - 1][0] + st[pos - 1][2]) * x - ed[pos + 1][0] * y;
res = res + st[pos - 1][1] * y + (ed[pos + 1][1] + ed[pos + 1][2]) * x;
ans = min(ans, res);
}
}
else {
for(int i = g.size() - 1; i >= 0; --i){
int pos = g[i];
res = res - st[pos - 1][0] * x - (ed[pos + 1][0] + ed[pos + 1][2]) * y;
res = res + (st[pos - 1][1] + st[pos - 1][2]) * y + ed[pos + 1][1] * x;
ans = min(res, ans);
}
}
cout << ans;
return 0;
}
注意题目是要求怒气值最小,不是最大(大雾
E - Poman Numbers
贪心
给出一个长度为 (n) 的字符串,每个字符串实质上代表一个数字(2^{pos(i)}),现在需要寻找一种递归顺序,使得每个位置非负即正,递归规则如下:
[f(S)=-f(S[1, m])+f(S[m+1,|S|]) ]其中(m)是([l,r])中任意的一个位置,且每一步的(m)都可以独立选择
问能否公国某种递归顺序,使得整个序列之和为给定的值(sum)
结论:第(n)个位置的符号一定为正,第(n−1) 个位置的符号一定为负,其余 (n − 2) 个位置的符号随意
设最终我们得到的符号是(a_1,a_2,a_3,...,a_n)
-
(n=2)时,(a_1=-1,a_2=1)
-
(n>2)时
- 假设要使得左边第一个是(-1),递归处理((-a_1)(a_2,a_3,...,a_n))即可
- 假设要使得左边第一个是(1),找到从左边开始的第一个非负数记为(i),满足(a_{i-1}=1,a_i=-1)则递归子问题((-a_1,-a_2,...,-a_{i-1},-a_i)(a_{i+1},...,a_{n}))即(a_i)最后一定是(1),且(a_1.a_2,...a_{i-1})都能递归成正数
目前,问题就变为了前(n-2)个数得到剩余的数(X)(减去最后两个数),贪心选取即可。
const int N = 1e6 + 2020;
int f[N], n, cnt[N], T;
char s[N];
signed main()
{
f[0] = 1;
for(int i = 1; i <= 26; ++i) f[i] = f[i - 1] * 2;
n = read(), T = read();
scanf("%s", s + 1);
T -= f[s[n] - 'a'] - f[s[n - 1] - 'a'];
for(int i = 1; i <= n - 2; ++i) T += f[s[i] - 'a'], cnt[s[i] - 'a']++;
for(int i = 25; i >= 0; --i) {
while(cnt[i] && T >= 2 * f[i]){
T -= 2 * f[i];
cnt[i]--;
}
}
if(T) cout << "NO";
else cout << "YES";
return 0;
}
F - The Thorny Path
贪心
又是贪心,更难了.....