牛客周赛 Round 62
A-小红的字符移动
签到题
代码
#include <iostream>
using namespace std;
int main()
{
string s;
cin >> s;
swap(s[0], s[1]);
cout << s;
}
B-小红的数轴移动
一道模拟题吧,按题意要求进行操作。
代码
#include <iostream>
using namespace std;
typedef long long ll;
const int N = 1e5 + 10;
int a[N];
int main()
{
int n, x;
cin >> n >> x;
ll res = 0;
while (n --)
{
if (x == 0) break;
ll k;
cin >> k;
if (x > 0) x -= k;
else x += k;
res += k;
}
cout << res;
return 0;
}
C-小红的圆移动
题目看太快看歪了,赛时解法对的,但做了一堆无效操作。
思路
题意要求包含原点的圆不超过k个,所以枚举每个圆,通过圆心到原点的距离和半径比较,判断是否包含圆心。记录包含圆心的圆的个数设为cnt,cnt <= k则直接输出0,不需要进行移动;cnt > k需要移动cnt - k个圆,移动代价是距离乘圆的面积,可以在枚举圆的时候,将包含原点的圆移动的代价存下来,进行排序,将代价较小的圆优先移动。
代码
#include <iostream>
#include <algorithm>
#include <cmath>
#define fi first
#define se second
using namespace std;
typedef long long ll;
const double pi = acos(-1);
const int N = 1e5 + 10;
pair<double, ll> a[N];
double b[N];
double S(ll r)
{
return pi * r * r;
}
double p(double x, ll r)
{
return fabs(x) * S(r);
}
int main()
{
int n, k;
cin >> n >> k;
int cnt = 0;
for (int i = 0; i < n; i ++)
{
ll x, y, r;
cin >> x >> y >> r;
double d = sqrt(x * x + y * y);
if (d - r < 0) b[cnt ++] = p(d - r, r);
}
if (cnt <= k) {
cout << 0;
}
else {
sort(b, b + cnt);
double res = 0;
for (int i = 0; i < cnt - k; i ++)
res += b[i];
printf("%.12lf", res);
}
return 0;
}
D-小红的树上移动
思路
注意题目说当前节点的邻点中不存在白色节点,则停止移动,根据树的定义,容易知道,停下移动的节点就是叶子节点,且移动过程必然是从根节点一路走到叶子节点。则当前路径的红色节点数量的期望就等于路径节点数量*选择走这条路径的概率,概率求法就是每个分叉点的概率之积。最终答案就是将所有叶子节点的期望求和。
代码
赛时bfs写法
#include <iostream>
#include <vector>
#include <queue>
using namespace std;
typedef long long ll;
const int mod = 1e9 + 7;
const int N = 1e5 + 10;
int n;
vector<int> g[N];
ll sz[N];
ll p[N];
bool st[N];
ll qmi(ll a, ll b)
{
ll res = 1;
while (b)
{
if (b & 1) res = res * a % mod;
a = a * a % mod;
b >>= 1;
}
return res;
}
ll inv(ll x)
{
return qmi(x, mod - 2);
}
void bfs()
{
queue<int> q;
q.push(1);
sz[1] = 1;
while (!q.empty())
{
int t = q.front();
q.pop();
st[t] = 1;
for (int i : g[t])
{
if (st[i]) continue;
sz[i] = sz[t] + 1;
p[i] = p[t] * (g[t].size() - (t != 1)) % mod;
q.push(i);
}
}
}
int main()
{
cin >> n;
for (int i = 1; i <= n; i ++) p[i] = 1;
for (int i = 1; i < n; i ++)
{
int u, v;
cin >> u >> v;
g[u].push_back(v);
g[v].push_back(u);
}
bfs();
ll res = 0;
for (int i = 2; i <= n; i ++)
if (g[i].size() == 1)
res = (res + sz[i] * inv(p[i]) % mod) % mod;
cout << res;
return 0;
}
dfs代码
#include <iostream>
#include <vector>
#include <queue>
using namespace std;
typedef long long ll;
const int mod = 1e9 + 7;
const int N = 1e5 + 10;
int n;
vector<int> g[N];
ll sz[N];
ll p[N];
bool st[N];
ll qmi(ll a, ll b)
{
ll res = 1;
while (b)
{
if (b & 1) res = res * a % mod;
a = a * a % mod;
b >>= 1;
}
return res;
}
ll inv(ll x)
{
return qmi(x, mod - 2);
}
int dfs(int u, int fa)
{
int cnt = 0, sum = 0;
for (int i : g[u])
{
if (i == fa) continue;
cnt ++;
sum = (sum + dfs(i, u)) % mod;
}
return (1 + sum * inv(cnt) % mod) % mod;
}
int main()
{
cin >> n;
for (int i = 1; i < n; i ++)
{
int u, v;
cin >> u >> v;
g[u].push_back(v);
g[v].push_back(u);
}
cout << dfs(1, 0);
return 0;
}
EF-小红的中位数查询
一道主席树的板子题吧。看下官方的题解吧,讲的太好了。
当然也可以用划分树。。。
代码
#include <iostream>
#include <algorithm>
#include <vector>
using namespace std;
const int N = 1e5 + 10;
int n, m;
int a[N];
vector<int> nums;
struct node {
int l, r;
int cnt;
} tr[N * 4 + N * 17];
int root[N], idx;
int find(int x)
{
return lower_bound(nums.begin(), nums.end(), x) - nums.begin();
}
int build(int l, int r)
{
int p = ++ idx;
if (l == r) return p;
int mid = l + r >> 1;
tr[p].l = build(l, mid), tr[p].r = build(mid + 1, r);
return p;
}
int insert(int p, int l, int r, int x)
{
int q = ++ idx;
tr[q] = tr[p];
if (l == r)
{
tr[q].cnt ++;
return q;
}
int mid = l + r >> 1;
if (x <= mid) tr[q].l = insert(tr[p].l, l, mid, x);
else tr[q].r = insert(tr[p].r, mid + 1, r, x);
tr[q].cnt = tr[tr[q].l].cnt + tr[tr[q].r].cnt;
return q;
}
int query(int q, int p, int l, int r, int k)
{
if (l == r) return r;
int cnt = tr[tr[q].l].cnt - tr[tr[p].l].cnt;
int mid = l + r >> 1;
if (k <= cnt) return query(tr[q].l, tr[p].l, l, mid, k);
else return query(tr[q].r, tr[p].r, mid + 1, r, k - cnt);
}
int main()
{
cin >> n >> m;
for (int i = 1; i <= n; i ++)
{
cin >> a[i];
nums.push_back(a[i]);
}
sort(nums.begin(), nums.end());
nums.erase(unique(nums.begin(), nums.end()), nums.end());
root[0] = build(0, nums.size() - 1);
for (int i = 1; i <= n; i ++)
root[i] = insert(root[i - 1], 0, nums.size() - 1, find(a[i]));
while (m --)
{
int l, r, k;
cin >> l >> r;
k = (r - l + 2) / 2;
cout << nums[query(root[r], root[l - 1], 0, nums.size() - 1, k)] << '\n';
}
return 0;
}
G-小红的数轴移动(二)
01背包。做法和蓝桥的砝码称重有点像,这里改求选择最少操作到达目标值,多一个求选择的先后序。
代码
#include <iostream>
#include <algorithm>
#include <cstring>
#include <set>
#include <vector>
using namespace std;
const int M = 10000;
const int N = 1e5 + 10;
int a[N], dp[110][M * 2];
vector<int> hp[M];
int n, k;
set<int> v;
int main()
{
cin >> n >> k;
int sum = 0;
for (int i = 1; i <= n; i ++)
{
cin >> a[i];
sum += a[i];
hp[a[i]].push_back(i);
v.insert(i);
}
memset(dp, 0x3f, sizeof dp);
dp[0][M + k] = 0;
for (int i = 1; i <= n; i ++)
{
for (int j = -M; j <= M; j ++) dp[i][M + j] = dp[i - 1][M + j];
for (int j = -M; j <= M; j ++)
{
if (j + a[i] <= M) dp[i][j + M + a[i]] = min(dp[i][j + M + a[i]], dp[i - 1][j + M] + a[i]);
if (j - a[i] >= -M) dp[i][j + M - a[i]] = min(dp[i][j + M - a[i]], dp[i - 1][j + M] + a[i]);
}
}
if (dp[n][M] == 0x3f3f3f3f) {
cout << sum << '\n';
for (int i = 1; i <= n; i ++) cout << i << ' ';
return 0;
}
vector<int> l, r, ans;
int res = dp[n][M], j = 0;
cout << res << '\n';
for (int i = n; i; i --)
{
if (dp[i][j + M] == dp[i - 1][j + M]) continue;
if (j + a[i] <= M && dp[i][j + M] == dp[i - 1][j + M + a[i]] + a[i]) {
l.push_back(hp[a[i]].back());
v.erase(hp[a[i]].back());
hp[a[i]].pop_back();
j += a[i];
res -= a[i];
}
if (j - a[i] >= -M && dp[i][j + M] == dp[i - 1][j + M - a[i]] + a[i]) {
r.push_back(hp[a[i]].back());
v.erase(hp[a[i]].back());
hp[a[i]].pop_back();
j -= a[i];
res -= a[i];
}
}
while (k != 0)
{
if (k < 0) {
k += a[r.back()];
ans.push_back(r.back());
r.pop_back();
}
else {
k -= a[l.back()];
ans.push_back(l.back());
l.pop_back();
}
}
for (int i : ans) cout << i << ' ';
for (int j : v) cout << j << ' ';
return 0;
}
另一个做法,图论。
将区间偏移100,考虑(100, 200]内每个点i是由i-a[i]的点转移过来的,边权是a[i],[0, 100)内每个点i是由i+a[i]的点转移过来的,边权是a[i]。然后以x+100作为起点,做dijkstra求最短路。注意跑当前路径的时候,每个点只能选一遍,同时将前驱节点记录下来。
#include <iostream>
#include <vector>
#include <queue>
#include <cstring>
#include <algorithm>
using namespace std;
typedef long long ll;
const ll INF = 0x3f3f3f3f3f3f3f3f;
const int N = 2e5 + 10;
struct node {
ll v, w, id;
};
struct edge {
ll v, w, id;
vector<bool> vis;
bool operator < (const edge& a) const {
return w > a.w;
}
};
int n, x;
ll a[N], dist[N], pre[N], pred[N];
bool st[N], vst[N];
vector<node> g[N];
void dijkstra()
{
memset(dist, 0x3f, sizeof dist);
priority_queue<edge> pq;
dist[x + 100] = 0;
vector<bool> vis(n + 1, 0);
pq.push({x + 100, 0, 0, vis});
while (!pq.empty())
{
auto [u, w, fa, vv] = pq.top();
pq.pop();
if (vst[u]) continue;
vst[u] = 1;
for (auto [v, c, id] : g[u])
{
if (dist[v] > w + c && !vv[id])
{
dist[v] = w + c;
pre[v] = u;
pred[v] = id;
vector<bool> vs = vv;
vs[id] = 1;
pq.push({v, w + c, id, vs});
}
}
}
}
int main()
{
cin >> n >> x;
ll sum = 0;
for (int i = 1; i <= n; i ++)
{
cin >> a[i];
sum += a[i];
for (int j = 0; j <= 200; j ++)
if (j > 100) g[j].push_back({j - a[i], a[i], i});
else if (j < 100) g[j].push_back({j + a[i], a[i], i});
}
dijkstra();
if (dist[100] == INF) {
cout << sum << '\n';
for (int i = 1; i <= n; i ++) cout << i << ' ';
return 0;
}
cout << dist[100] << '\n';
int ne = 100;
vector<int> ans;
if (pre[ne]) {
while (ne != x + 100)
{
ans.push_back(pred[ne]);
st[pred[ne]] = 1;
ne = pre[ne];
}
}
reverse(ans.begin(), ans.end());
for (int i = 1; i <= n; i ++)
if (!st[i]) ans.push_back(i);
for (int i : ans) cout << i << ' ';
return 0;
}

浙公网安备 33010602011771号