牛客周赛 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;
}
posted @ 2024-10-01 01:02  Natural_TLP  阅读(38)  评论(0)    收藏  举报