2020牛客暑期多校训练营(第九场)
| 题号 | A | B | C | D | E | F | G | H | I | J | K | L |
|---|---|---|---|---|---|---|---|---|---|---|---|---|
| 赛中 | 🎈 | 🎈 | 🎈 | 🎈 | 💭 | |||||||
| 赛后 | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ |
A - Groundhog and 2-Power Representation
题意
计算特定的表达式,答案范围为\([10,10^{180}]\)。
分析
利用大数和栈模拟即可,也可以转化为合法表达式,直接用Python的eval()方法计算。
代码
s = input()
st = [0]
for c in s:
if c is '(' or c is '+':
st.append(c)
elif c is ')':
y = 0
while True:
x = st.pop()
if x is '+':
continue
elif x is '(':
break
else:
y += int(x)
x = st.pop()
st.append(x ** y)
else:
x = st.pop()
if x not in ['(', ')', '+']:
x = int(x) * 10 + int(c)
st.append(int(x))
else:
st.append(x)
st.append(int(c))
ans = 0
for x in st:
if x is not '+':
ans += x
print(ans)
B - Groundhog and Apple Tree
题意
给出一棵\(n\)个结点的树,到达第\(i\)个结点能够恢复\(a_i\)点体力(每个结点仅能恢复\(1\)次体力),每次经过第\(i\)条边需要消耗\(w_i\)点体力(每条边仅能经过\(2\)次),每休息\(1\)秒可以恢复\(1\)点体力(在任意结点处均可休息),体力时刻不可小于\(0\),问从\(1\)号点出发,初始体力为\(0\),遍历完所有结点并回到\(1\)号点最少需要休息多少秒?(\(1\le n\le 10^5, \sum n \le 10^6, 0 \le a_i, w_i \lt 2^{31}\))
分析
首先有一个重要的推论,那就是 在起点把需要休息恢复的体力都恢复够再出发一定更优。考虑如果需要在中途休息恢复体力,那么显然在起点先恢复好这部分体力再出发只会令结果不变或更优。
则可以设:
对于当前结点\(u\),其儿子为\(v_i\),连边边权为\(w_i\),则结合\(dp[v_i]\)和\(sum[v_i]\)可以进一步求得 从\(u\)开始遍历完以\(v_i\)为根的子树后返回\(u\) 所需要 预先支付的代价\(in[v_i]\) 和 最后得到的收益\(out[v_i]\)。
接下来就是决定子树遍历的顺序,为使得\(dp[u]\)(借用的代价)最小,显然应当先遍历\(in[v_i]\le out[v_i]\)的子树,且要按\(in[v_i]\)从小到大遍历;随后遍历\(in[v_i]\gt out[v_i]\)的子树,且要按\(out[v_i]\)从大到小遍历。
最终\(dp[1]\)即为答案,时间复杂度\(O(n\log n)\)。
代码
#include <bits/stdc++.h>
using namespace std;
typedef long long LL;
typedef pair<int, int> PII;
const int INF = 0x3f3f3f3f;
const int MOD = 998244353;
const int maxn = 1e5 + 10;
int n, a[maxn];
struct edge
{
int v;
int w;
};
struct sub_tree
{
LL in;
LL out;
};
bool cmp1(const sub_tree x, const sub_tree y)
{
if(x.in != y.in)
return x.in < y.in;
else
return x.out > y.out;
}
bool cmp2(const sub_tree x, const sub_tree y)
{
if(x.out != y.out)
return x.out > y.out;
else
return x.in < y.in;
}
vector<edge> g[maxn];
LL dp[maxn], sum[maxn];
void init()
{
for(int i = 1; i <= n; i++)
{
dp[i] = sum[i] = 0;
g[i].clear();
}
}
void DFS(int u, int fa)
{
vector<sub_tree> b, c;
sum[u] = a[u];
for(auto e : g[u])
{
int v = e.v, w = e.w;
if(v == fa)
continue;
DFS(v, u);
sum[u] += sum[v] - (long long)2 * w;
sub_tree t;
if(dp[v] + sum[v] >= w)
t = sub_tree{w + dp[v], dp[v] + sum[v] - w};
else
t = sub_tree{(long long)2 * w - sum[v], 0};
if(t.in <= t.out)
b.push_back(t);
else
c.push_back(t);
}
sort(b.begin(), b.end(), cmp1);
sort(c.begin(), c.end(), cmp2);
LL cur = a[u];
for(auto t : b)
{
if(cur < t.in)
{
dp[u] += t.in - cur;
cur = t.in;
}
cur = cur - t.in + t.out;
}
for(auto t : c)
{
if(cur < t.in)
{
dp[u] += t.in - cur;
cur = t.in;
}
cur = cur - t.in + t.out;
}
}
int main()
{
int T;
scanf("%d", &T);
while(T--)
{
scanf("%d", &n);
init();
for(int i = 1; i <= n; i++)
scanf("%d", &a[i]);
for(int i = 1; i <= n - 1; i++)
{
int u, v, w;
scanf("%d %d %d", &u, &v, &w);
g[u].push_back(edge{v, w});
g[v].push_back(edge{u, w});
}
DFS(1, 0);
printf("%lld\n", dp[1]);
}
return 0;
}
C - Groundhog and Gaming Time
题意
思路
代码
D - Groundhog and Golden Apple
题意
思路
代码
E - Groundhog Chasing Death
题意
思路
代码
F - Groundhog Looking Dowdy
题意
共\(n\)天,每天有\(k_i\)件物品,分别有权值\(a_{i,j}\),问从中选\(m\)天,每天选\(1\)件物品,所选的\(m\)件物品中权值的最大差值最小为多少?(\(1\le a_{i,j} \le 10^9, 1\le m \le n\le 10^6, k_i \ge 1 且 \sum k_i \le 2\cdot 10^6\))
分析
将物品按权值排序,对于一个权值区间,若其中包含的物品对应天数种类\(\ge m\),则为一个合法区间。所以只需要找到一个最小的合法区间,该过程显然具有单调性,可用尺取法在\(O(n)\)的时间复杂度内解决。
代码
#include <bits/stdc++.h>
using namespace std;
typedef long long LL;
typedef pair<int, int> pii;
#define fir first
#define sec second
const int INF = 0x3f3f3f3f;
const int MOD = 998244353;
const int maxn = 2e6 + 10;
int n, m,N;
pii X[maxn];
int cnt[maxn], tot;
int main()
{
scanf("%d%d", &n, &m);
for(int i = 1; i <= n; i++)
{
int k, d;
scanf("%d", &k);
for(int j = 1; j <= k; j++)
{
scanf("%d", &d);
X[++N] = pii(d,i);
}
}
sort(X + 1,X + N + 1);
if (m == 0) puts("0");
else {
int l = 1, r = 0, tot = 0, ans = 1000000000;
cnt[X[r].sec]++;
for (int l = 1; l <= N; l++) {
while (tot < m && r < N) {
++r;
if (cnt[X[r].sec] == 0) tot++;
cnt[X[r].sec]++;
}
if (tot >= m) ans = min(ans,X[r].fir - X[l].fir);
cnt[X[l].sec]--;
if (cnt[X[l].sec] == 0) tot--;
}
printf("%d\n", ans);
}
return 0;
}
G - Groundhog Playing Scissors
题意
思路
代码
H - Groundhog Speaking Groundhogish
题意
思路
代码
I - The Crime-solving Plan of Groundhog
题意
思路
代码
J - The Escape Plan of Groundhog
题意
给出一个\(n\times m\)的01矩阵,问满足下列条件的子矩阵的个数:(\(1\le n, m \le 500\))
- 子矩阵边界位置全为\(1\);
- 子矩阵内部(不含边界)的\(0\)、\(1\)数量差的绝对值不大于\(1\);
- 子矩阵的长和宽均大于\(1\);
分析
考虑枚举上、下边界所在行的位置,遍历行时判断上、下边界\(1\)的连续性即可保证上、下边界为全\(1\),预处理前缀\(0\)、\(1\)数量之和,即可\(O(1)\)判断左、右边界是否为全\(1\),遍历的时候同时维护一下区域内的前缀\(0\)、\(1\)数量差之和,保证数量差的绝对值不大于\(1\)。
最终时间复杂度\(O(n^2m)\)
代码
#include <bits/stdc++.h>
using namespace std;
typedef long long LL;
typedef pair<int, int> PII;
const int INF = 0x3f3f3f3f;
const int MOD = 998244353;
const int maxn = 500 + 10;
const int fix = 300000;
int n, m, a[maxn][maxn];
int sum[2][maxn][maxn], cnt[1000010];
vector<int> v;
void clear()
{
for(auto d : v)
cnt[d]--;
v.clear();
}
int main()
{
scanf("%d %d", &n, &m);
for(int i = 1; i <= n; i++)
{
for(int j = 1; j <= m; j++)
{
scanf("%d", &a[i][j]);
sum[0][i][j] = sum[0][i-1][j] + (a[i][j] == 0 ? 1 : 0); // 纵向1数量前缀和
sum[1][i][j] = sum[1][i-1][j] + (a[i][j] == 1 ? 1 : 0); // 纵向0数量前缀和
}
}
LL ans = 0;
for(int i = 1; i <= n; i++)
{
for(int j = i + 1; j <= n; j++)
{
int d = fix; // 区域内前缀0、1数量之差
for(int k = 1; k <= m; k++)
{
if(!a[i][k] || !a[j][k]) // 上、下边界的1断连
{
clear();
d += (sum[0][j-1][k] - sum[0][i][k]) - (sum[1][j-1][k] - sum[1][i][k]);
}
else if(sum[0][j-1][k] - sum[0][i][k] == 0) // 右边界全1
{
ans += cnt[d-1] + cnt[d] + cnt[d+1];
d += (sum[0][j-1][k] - sum[0][i][k]) - (sum[1][j-1][k] - sum[1][i][k]);
//printf("i = %d j = %d k = %d ans = %lld", i, j, k, ans);
cnt[d]++;
v.push_back(d);
}
else
d += (sum[0][j-1][k] - sum[0][i][k]) - (sum[1][j-1][k] - sum[1][i][k]);
// printf(" d = %d\n", d - fix);
}
clear();
}
}
printf("%lld\n", ans);
return 0;
}

浙公网安备 33010602011771号