Educational Codeforces Round 89 (Rated for Div. 2)
A.Shovels and Swords
题意:
已知有a个木棍和b个钻石,做一个钻石铲要2个木棍和1个钻石,做一把钻石剑需要1个木棍和2个钻石,问最多总共能做多少工具。
思路
刚读完题满脑子都是:这也太MC了吧 这可咋搞,然后就开始瞎推公式了QUQ。这个就是写出来吐槽一下自己的,可以跳过QUQ。
首先设钻石铲x把,钻石剑b把,依题意易得
a
>
=
2
∗
x
+
y
b
>
=
x
+
2
∗
y
a>=2*x+y\\b>=x+2*y
a>=2∗x+yb>=x+2∗y
公式两边相加变成
(
a
+
b
)
/
3
>
=
x
+
y
(a+b)/3>=x+y
(a+b)/3>=x+y
得到x+y的最大值。也不懂昨晚在想什么,写到这里就脑子一抽地交上去莽(WA)了一发Orz这个公式,实际上a,b的数量可能无法达到这个最大值,所以再对不满足的情况处理一下就出了。
Code
#include <bits/stdc++.h>
#define re read()
#define ll long long
#define rep(a, b, c) for(int a = b; a <= c; a++)
#define per(a, b, c) for(int a = b; a >= c; a--)
using namespace std;
int read()
{
int num = 0; bool f = 0; char ch = getchar();
while(ch < '0' || ch > '9') {f = (ch == '-'); ch = getchar();}
while(ch >= '0' && ch <= '9') {num = (num << 1) + (num << 3) + ch - '0'; ch = getchar();}
return f? -num : num;
}
int main()
{
per(T, re, 1)
{
int a = re, b = re;
if(!a || !b) {puts("0"); continue;}
int ans = (a + b) / 3;
if(a <= ans) ans = min(a, b / 2);
if(b <= ans) ans = min(ans, min(b, a / 2));
printf("%d\n", ans);
}
return 0;
}
B.Shuffle
题意
给定n个数,第x个数为1,其余全为0。共给出m个区间,每次可将区间内任意两数位置交换,求有多少个位置在m次操作后可以为1。
思路
(废话一堆,可以直接看下一段)第一眼看完以为是输出任意一位最后可以是1的,差点就莽了,然后又去看了一波,看完人傻了,以为今晚就要跪在这里了QWQQQ然后对样例手做了一下,第一反应是做区间覆盖,感觉不太会写就又仔细想了一下。
注意,题意是每次总共只会有一个1,最后的答案是所有的可能情况之和。
题目后面补充了可以选择两个相同的数字进行交换(就是相当于可以不操作)所以,每次操作时,只要这个区间内有1存在,那么这个区间内每一个数字都可以在m次操作后为1。因此易知可以得到1的位置是一段连续区间(假设所给区间A与之前的连续区间交集为空,则区间A均不出现1)
所以我们用 l l l和 r r r记录连续区间的左右范围(显而易见 a n s = r − l + 1 ans=r-l+1 ans=r−l+1),对于每次所给的区间,若其与 [ l , r ] [l,r] [l,r]没有交集,则这个区间均不能有1。若有交,则可以用其对原连续区间进行更新。
Code
#include <bits/stdc++.h>
#define re read()
#define ll long long
#define rep(a, b, c) for(int a = b; a <= c; a++)
#define per(a, b, c) for(int a = b; a >= c; a--)
using namespace std;
int read()
{
int num = 0; bool f = 0; char ch = getchar();
while(ch < '0' || ch > '9') {f = (ch == '-'); ch = getchar();}
while(ch >= '0' && ch <= '9') {num = (num << 1) + (num << 3) + ch - '0'; ch = getchar();}
return f? -num : num;
}
int n, t, m, l, r;
int main()
{
per(T, re, 1)
{
n = re, t = re, m = re;
l = r = t;
rep(i, 1, m)
{
int a = re, b = re;
if(a > r || b < l) continue;
l = min(l, min(t, a)), r = max(r, max(t, b));
}
printf("%d\n", r - l + 1);
}
return 0;
}
C.Palindromic Paths
题意
给定一个n*m的矩阵,矩阵中只有0/1,每次只能向左或右移动,求需要多少次更改,才可以使(1,1)->(n,m)的每一条路径均为回文。
思路
显而易见符合答案的矩阵,在到(1,1)和(n,m)的距离相等的点(距离相同的就是回文数的每一位),所对应的格子的值相等。所以题目就转换为把到(1,1)和(n,m)距离相等的所有点视为一组,并将这组点全部转换为0/1所需的最小次数,求和一下就是所要的答案。
因为只会笨笨的直观求和,所以画个图示意一下QUQ
这题的距离其实就是曼哈顿距离。如下图所示,每一条线上的每一个点到(1,1)的距离都相等(即
i
+
j
i+j
i+j相等)。利用回文串的思路,只需要把点按到(1,1)的距离相等的点划分为同一组进行统计即可。统计的部分写个三四条线就能找到规律了。
注意:因为回文中间的一位并不影响回文的性质,所以最后求和的时候不需要对这组进行统计QUQ
我太菜了,这题主要的时间都花在推回文位置和求和的循环上QUQ
Code
#include <bits/stdc++.h>
#define re read()
#define ll long long
#define rep(a, b, c) for(int a = b; a <= c; a++)
#define per(a, b, c) for(int a = b; a >= c; a--)
using namespace std;
int read()
{
int num = 0; bool f = 0; char ch = getchar();
while(ch < '0' || ch > '9') {f = (ch == '-'); ch = getchar();}
while(ch >= '0' && ch <= '9') {num = (num << 1) + (num << 3) + ch - '0'; ch = getchar();}
return f? -num : num;
}
int n, m, ans;
int a[66], b[66];
int main()
{
per(T, re, 1)
{
n = re, m = re; ans = 0;
rep(i, 0, n + m) a[i] = b[i] = 0;
rep(i, 1, n)
{
rep(j, 1, m)
{
int x = re;
a[i + j] += x, b[i + j] += (!x);
}
}
rep(i, 2, (n + m + 1) / 2)
ans += min(a[i] + a[n + m + 2 - i], b[i] + b[n + m + 2 - i]);
printf("%d\n", ans);
}
return 0;
}