比赛地址
弱校连萌寒假套题一
A. The Big Dance
题意:
有n头牛,初始情况下都在一起,编号1~n。每次将一群牛按照编号均等分为前后两部分,后面部分的牛不能多于前面部分的。如果分出了两头牛,则这两头牛去跳舞,如果分出了一头牛,则这头牛不能跳舞,如果分出的牛数量大于2,则继续分,问跳舞的牛编号乘积之和。
n <= 2200。
题解:
考察你会不会递归,模拟分解过程即可。时间复杂度O(n)。
代码:
#include <cstdio>
int n, ans;
void dfs(int l, int r)
{
if(l == r)
return;
if(r - l == 1)
ans += l * r;
int m = l + r >> 1;
dfs(l, m);
dfs(m + 1, r);
}
int main()
{
scanf("%d", &n);
dfs(1, n);
printf("%d\n", ans);
return 0;
}
B. Lonesome Partners
题意:
给定n头牛的坐标,求最远的两头牛的编号,保证解唯一。
n <= 500, 0 <= 坐标 <= 5000。
题解:
枚举两头牛,计算距离即可,避免浮点数误差可以只计算距离的平方。时间复杂度O(n^2)。
代码:
#include <cstdio>
const int maxn = 500;
int n, x[maxn], y[maxn], ans, xpos, ypos;
int main()
{
scanf("%d", &n);
for(int i = 0; i < n; ++i)
scanf("%d%d", x + i, y + i);
for(int i = 0; i < n; ++i)
for(int j = i + 1; j < n; ++j)
if(ans < (x[i] - x[j]) * (x[i] - x[j]) + (y[i] - y[j]) * (y[i] - y[j]))
{
ans = (x[i] - x[j]) * (x[i] - x[j]) + (y[i] - y[j]) * (y[i] - y[j]);
xpos = i;
ypos = j;
}
printf("%d %d\n", xpos + 1, ypos + 1);
return 0;
}
C. Shorter Musical Notes
题意:
有n首曲子,编号1~n,从0时刻开始依次演奏,每首曲子有一个时长b。有q个询问,问某个时刻演奏的是第几首曲子。
n <= 10000, q <= 50000, 总时间t <= 1200000。
题解:
计算出每首曲子的起止时间,依次枚举曲子的话时间复杂度为O(nq),超时;
计算出每个时刻演奏的曲子是什么,每次直接询问的话时间复杂度为O(t+q),可行;
根据每首曲子的终止时间是单调递增的从而二分查找答案的话时间复杂度为O(qlogn),可行。
代码:
#include <cstdio>
const int maxn = 1e4 + 1;
int n, q, cnt[maxn];
int main()
{
scanf("%d%d", &n, &q);
for(int i = 1; i <= n; ++i)
{
scanf("%d", cnt + i);
cnt[i] += cnt[i - 1];
}
while(q--)
{
int x, l = 1, r = n + 1, m;
scanf("%d", &x);
while(l < r)
{
m = l + r >> 1;
if(cnt[m] > x)
r = m;
else
l = m + 1;
}
printf("%d\n", l);
}
return 0;
}
D. Bobsledding
题意:
有一条长度为L的路,牛牛初始在坐标为0的位置,速度为1,每前进1单位距离,可以使速度的变化不超过1,现在限定了n个位置的最高速度,求牛牛在前进到坐标为L的位置的过程中最高速度是多少。
L <= 10 ^ 9,n <= 10 ^ 5。
题解:
可以考虑先算出被限制点真正的限速,然后相邻的限制点之间先加速再减速即可,可达到的最高速度为相邻点限速之和加时间差的一半(可以认为是时间分别用来提高从两点增加的速度,提高到的最高点)。
被限制点真正的限速可以利用两遍dp解决,第一遍可以逆着dp,算出向后加速的限速,第二遍顺着dp,算出从前加速的限速,取最小值即可。注意隐含的起点与终点。
时间复杂度O(n)。
代码:
#include <cstdio>
#include <algorithm>
using namespace std;
const int maxn = 1e5 + 10;
int n, l, ans;
struct Node
{
int t, s;
bool operator < (const Node &x) const
{
return t < x.t;
}
} a[maxn];
int main()
{
scanf("%d%d", &l, &n);
a[0].t = 0, a[0].s = 1;
for(int i = 1; i <= n; ++i)
scanf("%d%d", &a[i].t, &a[i].s);
sort(a + 1, a + n + 1);
a[n + 1].t = l, a[n + 1].s = l + 1;
for(int i = n; i; --i)
if(a[i].s > a[i + 1].s + a[i + 1].t - a[i].t)
a[i].s = a[i + 1].s + a[i + 1].t - a[i].t;
for(int i = 1; i <= n + 1; ++i)
{
if(a[i].s > a[i - 1].s + a[i].t - a[i - 1].t)
a[i].s = a[i - 1].s + a[i].t - a[i - 1].t;
int tmp = (long long)a[i].s + a[i - 1].s + a[i].t - a[i - 1].t >> 1;
if(ans < tmp)
ans = tmp;
}
printf("%d\n", ans);
return 0;
}
E. Music Notes
题意:
有n首曲子,编号1~n,从0时刻开始依次演奏,每首曲子有一个时长b。有q个询问,问某个时刻演奏的是第几首曲子。
n <= 50000, q <= 50000, 总时间t <= 500000000。
题解:
计算出每首曲子的起止时间,依次枚举曲子的话时间复杂度为O(nq),超时;
计算出每个时刻演奏的曲子是什么,每次直接询问的话时间复杂度为O(t+q),超时;
根据每首曲子的终止时间是单调递增的从而二分查找答案的话时间复杂度为O(qlogn),可行。
代码:
#include <cstdio>
const int maxn = 5e4 + 1;
int n, q, cnt[maxn];
int main()
{
scanf("%d%d", &n, &q);
for(int i = 1; i <= n; ++i)
{
scanf("%d", cnt + i);
cnt[i] += cnt[i - 1];
}
while(q--)
{
int x, l = 1, r = n + 1, m;
scanf("%d", &x);
while(l < r)
{
m = l + r >> 1;
if(cnt[m] > x)
r = m;
else
l = m + 1;
}
printf("%d\n", l);
}
return 0;
}
F. Selfish Grazing
题意:
有n个区间,选出尽量多的区间互不相交,求区间个数。
n <= 50000。
题解:
贪心选取区间即可,按照区间右端点排序,从最左边开始选择即可,时间复杂度O(nlogn)。
代码:
#include <cstdio>
#include <algorithm>
using namespace std;
const int maxn = 5e4;
int n, ans;
struct Node
{
int s, e;
bool operator < (const Node &x) const
{
return e < x.e;
}
} a[maxn];
int main()
{
scanf("%d", &n);
for(int i = 0; i < n; ++i)
scanf("%d%d", &a[i].s, &a[i].e);
sort(a, a + n);
for(int i = 0, last = a[0].s; i < n; ++i)
if(a[i].s >= last)
{
++ans;
last = a[i].e;
}
printf("%d\n", ans);
return 0;
}
G. Cow Toll Paths
题意:
有n个点,m条边,点有点权,边有边权,定义两点间一条路径的长度为路径上边权之和加路径上点权最大值,询问k次两点间的最短距离。
n <= 250, m <= 10000, 权值 <= 100000。
题解:
不考虑点权的情况(g)就是直接floyd即可,若考虑点权的情况(f)则需要按照点权递增的顺序依次添加点进入floyd算法,时间复杂度O(n^3)。
代码:
#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std;
const int maxn = 251;
int n, m, q, pos[maxn], g[maxn][maxn], f[maxn][maxn];
struct Node
{
int id, c;
bool operator < (const Node &x) const
{
return c < x.c;
}
} a[maxn];
int main()
{
scanf("%d%d%d", &n, &m, &q);
for(int i = 1; i <= n; ++i)
{
a[i].id = i;
scanf("%d", &a[i].c);
}
sort(a + 1, a + n + 1);
for(int i = 1; i <= n; ++i)
pos[a[i].id] = i;
memset(g, 0x3f, sizeof g);
while(m--)
{
int u, v, w;
scanf("%d%d%d", &u, &v, &w);
if(w < g[u = pos[u]][v = pos[v]])
g[u][v] = g[v][u] = w;
}
for(int i = 1; i <= n; ++i)
g[i][i] = 0;
memset(f, 0x3f, sizeof f);
for(int k = 1; k <= n; ++k)
for(int i = 1; i <= n; ++i)
for(int j = 1; j <= n; ++j)
if(g[i][j] >= g[i][k] + g[k][j])
{
g[i][j] = g[i][k] + g[k][j];
int cost = max(a[i].c, max(a[j].c, a[k].c));
if(f[i][j] > g[i][j] + cost)
f[i][j] = g[i][j] + cost;
}
while(q--)
{
int u, v;
scanf("%d%d", &u, &v);
printf("%d\n", f[pos[u]][pos[v]]);
}
return 0;
}
H. Video Game Troubles
题意:
有一个体积为V的背包,和n组物品,每组物品里的每个物品有一个价值和一个体积,如果要选择将某个物品放入背包则还需另外的空间,但同一组的物品只需要一个额外的空间,求背包最多能装多少价值的物品。
n <= 50, V <= 100000, 每组物品数p <= 10, 每个物品的价值 <= 1000000。
题解:
按照正常的分组背包来做即可,每一组是一个01背包,如果要选物品则多加一个额外的物品。时间复杂度O(npV)。
代码:
#include <cstdio>
const int maxv = 1e5 + 1;
int n, v, f[maxv], g[maxv];
int main()
{
scanf("%d%d", &n, &v);
while(n--)
{
int m, vv, c, w;
scanf("%d%d", &vv, &m);
for(int i = vv; i <= v; ++i)
g[i] = f[i - vv];
while(m--)
{
scanf("%d%d", &c, &w);
for(int i = v; i >= c + vv; --i)
if(g[i] < g[i - c] + w)
g[i] = g[i - c] + w;
}
for(int i = vv; i <= v; ++i)
if(f[i] < g[i])
f[i] = g[i];
}
printf("%d\n", f[v]);
return 0;
}
小记
usaco月赛的题目并不难,主要考察思维。