Toyota Programming Contest 2024#2(AtCoder Beginner Contest 341) - AtCoder
B读不懂题卡了,F读假题卡了,开题开慢了rank++了
A - Print 341
题意:
打印一串交替出现的包含N个0,N+1个1的01串
代码:
void solve()
{
int n;
scanf("%d", &n);
for (int i = 1; i <= n; ++i)
printf("10");
printf("1");
}
B - Foreign Exchange
题意:
有N个国家的货币,每个国家的货币初始有Ai个,国家 i 的货币可以换为国家 i + 1 的货币,比例为Si : Ti,问最多能拥有多少国家N的货币
题解:
从前往后尽量换完换即可
LL a[N];
void solve()
{
int n;
scanf("%d", &n);
for (int i = 1; i <= n; ++i)
scanf("%lld", &a[i]);
for (int i = 1; i < n; ++i)
{
LL s, t;
scanf("%lld%lld", &s, &t);
t *= a[i] / s;
a[i + 1] += t;
}
printf("%lld\n", a[n]);
}
C - Takahashi Gets Lost
题意:
给出N行M列的图,'.'表示可以经过的格子,'#'表示不能经过的格子,给出一串指令集,包含LRUD四种指令(分别表示往左右上下走,具体解释见原题),问有多少个初始位置能满足执行以上一串指令时不经过'#'
题解:
500^3暴力即可
char op[N], mp[N][N];
int check(int x, int y)
{
int i = 1;
while (op[i])
{
if (mp[x][y] == '#')return 0;
if (op[i] == 'L')y -= 1;
if (op[i] == 'R')y += 1;
if (op[i] == 'U')x -= 1;
if (op[i] == 'D')x += 1;
++i;
}
return mp[x][y] == '.';
}
void solve()
{
int n, m, t;
scanf("%d%d%d%s", &n, &m, &t, op + 1);
for (int i = 0; i <= n + 1; ++i)
{
for (int j = 0; j <= m + 1; ++j)
mp[i][j] = '#';
}
for (int i = 1; i <= n; ++i)
{
getchar();
for (int j = 1; j <= m; ++j)
mp[i][j] = getchar();
}
int ans = 0;
for (int i = 1; i <= n; ++i)
{
for (int j = 1; j <= m; ++j)
ans += check(i, j);
}
printf("%d\n", ans);
}
D - Only one of two
题意:
找出第k个是n的倍数或者是m的倍数切不同时是n,m的倍数的数
题解:
关于x是第几个数可以直接通过容斥求得:f(x) = x / n + x / m - 2 * (x / lcm(n, m)),然后二分答案即可
LL n, m, k, lcm;
LL gcd(LL x, LL y)
{
return y ? gcd(y, x % y) : x;
}
LL check(LL x)
{
return x / n + x / m - 2 * (x / lcm);
}
void solve()
{
scanf("%lld%lld%lld", &n, &m, &k);
lcm = n * m / gcd(n, m);
LL l = 0, r = 2e18;
while (l < r)
{
LL mid = l + r >> 1;
if (check(mid) < k)
l = mid + 1;
else
r = mid;
}
printf("%lld\n", l);
}
E - Alternating String
题意:
给出一个长度为n的01串,当一个01串被称为好的必须符合以下条件:任意相邻数字不同(01交替出现)。你需要对这个01串进行m次询问,询问分为两种
1 L R:反转区间L到R内的元素(0变成1,1变成0)
2 L R:查询L到R的子串是否是好的
题解:
我们可以仅考虑对于每对相邻元素是否相同,不考虑具体每个元素是0还是1,对于修改操作仅会使得(l - 1, l)与(r, r + 1)这两对相邻元素是否相同的性质反转,我们可以用一个set存所有相同的相邻元素位置(如存左边的元素下标)。对于修改操作我们仅需查询set内是否已经存在l - 1,若存在则删除,不存在则加入,右边同理,这样维护这个集合。对于查询操作我们仅需查询set中是否不存在l, r - 1范围的元素即可(set是支持二分查找的,可以通过*st.lower_bound(l) >= r 求得)
char ch[N];
set<int>st;
void solve()
{
int n, m;
scanf("%d%d%s", &n, &m, ch + 1);
for (int i = 1; i < n; ++i)
{
if (ch[i] == ch[i + 1])
st.insert(i);
}
while (m--)
{
int op, l, r;
scanf("%d%d%d", &op, &l, &r);
if (op == 1)
{
if (st.find(l - 1) != st.end())
st.erase(l - 1);
else
st.insert(l - 1);
if (st.find(r) != st.end())
st.erase(r);
else
st.insert(r);
}
else
{
auto it = st.lower_bound(l);
if (it == st.end() || *it >= r)
printf("Yes\n");
else
printf("No\n");
}
}
}
F - Breakdown
题意:
给出一张n个点m条边的无向简单图,每个点都有一个权值Wi,初始每个点上面都放有Ai个棋子,你需要进行以下操作直至棋盘上没有棋子:
选择一个棋子,删除它,记改棋子所在点的编号为x,选择一个点集S,需要使得集合内的所有点与点x相邻,且,并在每个属于点集S的点放上一颗棋子
求最大的操作次数
应该也可以线段树做(可能能写类似于维护最大区段和的写法),但上面做法更简单就懒得想了
题解:
背包DP,我们可以对每个点求一个 Si 表示在这个点上放一个棋子最多能进行多少次操作,与它相连的 x 个点看为 x 个物品,每个物品 j 的重量为 wj ,价值为 sj ,当前节点的容量为 wi - 1 ,显然这是一个01背包问题。
其次我们需要按 Wi 的值从小到大依次求每个点的 Si ,因为当我们求 i 点的 Si 时只会用到 Wj < Wi 的点做背包,只有当所有 Wj < Wi 的点的 Sj 才能求 Si
int w[N];
PII t[N];
vector<int>e[N];
LL s[N];
void solve()
{
int n, m;
scanf("%d%d", &n, &m);
for (int i = 1; i <= m; ++i)
{
int u, v;
scanf("%d%d", &u, &v);
e[u].push_back(v);
e[v].push_back(u);
}
for (int i = 1; i <= n; ++i)
{
scanf("%d", &w[i]);
t[i] = { w[i],i };
}
sort(t + 1, t + 1 + n);
for (int i = 1; i <= n; ++i)
{
int u = t[i].second;
vector<LL>dp(w[u]);
for (auto v : e[u])
{
for (int j = w[u] - 1; j >= w[v]; --j)
dp[j] = max(dp[j], dp[j - w[v]] + s[v]);
}
for (auto j : dp)
s[u] = max(s[u], j + 1);
}
LL ans = 0;
for (int i = 1, x; i <= n; ++i)
scanf("%d", &x), ans += x * s[i];
printf("%lld\n", ans);
}
G - Highest Ratio
题意:
给出一个长度为N的数组,对于每个左端点求最大子段平均值
题解:
赛时不会做,赛后看群友说的:
记 Si 为前 i 个元素的前缀和,在平面上放N个点(i, Si),第 x 个点到第 y 个点连线的斜率即为区间(x + 1, y)的平均值(平均值 = (Sy - Sx) / (y - x) = 斜率),在这个图上求凸包即可
double cross(PLL x, PLL y)//向量叉乘,直接乘好像会爆longlong用了double
{
return (double)x.first * y.second - (double)x.second * y.first;
}
PLL operator-(PLL x, PLL y)//重载向量减法
{
return { x.first - y.first,x.second - y.second };
}
double fun(PLL v)//求向量斜率
{
return (double)v.second / v.first;
}
LL s[N];
double ans[N];
void solve()
{
int n;
scanf("%d", &n);
for (int i = 1; i <= n; ++i)
scanf("%d", &s[i]), s[i] += s[i - 1];
vector<PLL>v{ {n,s[n]} };
for (int i = n - 1; i >= 0; --i)
{
PLL x = { i,s[i] };
while (v.size() > 1)//典 凸包板子
{
PLL y = v.back(), z = v[v.size() - 2];
if (cross(y - x, z - y) < 0)break;
v.pop_back();
}
ans[i + 1] = fun(v.back() - x);
v.push_back({ i,s[i] });
}
for (int i = 1; i <= n; ++i)
printf("%.10lf\n", ans[i]);
}