牛客周赛62

A.小红的字符移动

https://ac.nowcoder.com/acm/contest/91177/A

1.思路 (AC ^ ^)

        直接交换字符串第一个位置和第二个位置即可,可以利用swap函数

B.小红的数轴移动(一)

https://ac.nowcoder.com/acm/contest/91177/B

1.思路 (AC ^ ^)

        所处位置大于0时减去对应步数,小于0时加上对应步数,直到到达原点,把所有距离加上输出

C.小红的圆移动

https://ac.nowcoder.com/acm/contest/91177/C

1.思路 (AC  ^ ^)

        若目前所包含的原点的圆的数量小于等于k,直接输出0,否则的话,将包含原点的圆移出原点所耗费的代价从小到大排序,移出相应的圆的个数使得包含的原点的圆的数量等于k即可,并累加代价即可

D.小红的树上移动

https://ac.nowcoder.com/acm/contest/91177/D

1.思路

        计算到每个结点的概率*权值(这里是1)的结果,并把它们相加即为最终的结果。或者计算到所有叶子结点的概率,并将到对应的叶子结点的概率*对应的深度并取和即为最终结果。需要注意的是,计算概率时,除法需要转化为乘法进行运算,即除以一个数x,等于乘以x的逆元

2.函数

(1)快速幂运算,计算a的b次方

int qpow(int a, int b){
    int t = 1;
    int y = a;
    while(b){
        if(b&1){
            t = (t*y)%p;
        }
        y = (y*y)%p;
        b>>=1;
    }
    return t;
}

(2)计算x的逆元

int ni(int x){
    return qpow(x,p-2)%p;
}

3.代码

#include <bits/stdc++.h>
#define int long long
using namespace std;

const int N = 1e5+10;
const int p = 1e9+7;
vector<int> a[N];       // 利用邻接表存储邻边的信息
int whe[N];             // 存储i结点是否遍历过
int ans[N];             // 存储到i结点的概率

int qpow(int a, int b){ // 快速幂运算
    int t = 1;
    int y = a;
    while(b){
        if(b&1){
            t = (t*y)%p;
        }
        y = (y*y)%p;
        b>>=1;
    }
    return t;
}

int ni(int x){          // 计算x的逆元
    return qpow(x,p-2)%p;
}

void bfs(){
    queue<int> q;       // 利用队列进行维护,进行bfs(广搜)
    q.push(1);          // 从结点1开始  
    whe[1] = 1;         // 结点1已遍历
    ans[1] = 1;         // 结点1的概率为1
    while(q.size()){    // 进行bfs
        int top = q.front();
        q.pop();
        for(int j=0;j<a[top].size();j++){   // 遍历当前结点的所有邻结点
            int lb = a[top][j];
            if(whe[lb]==1){     // 已遍历直接continue
                continue;
            }
            q.push(lb);         // 未遍历进入队列进行维护
            whe[lb] = 1;        // 并设置状态为已遍历
            if(top==1){         // 如果当前结点是1,那么到每个邻接点的概率为1/a[top].size()
                ans[lb] = ans[top] * ni(a[top].size())%p;
            }else{              // 否则的话,到邻接点的概率为ans[top]/a[top].size()-1
                ans[lb] = ans[top] * ni(a[top].size()-1)%p;
            }
        }
    }
}

signed main(){
    int n;
    cin>>n;
    for(int i=1;i<=n-1;i++){
        int s,e;
        cin>>s>>e;
        a[s].push_back(e);
        a[e].push_back(s);
    }
    bfs();
    int sum = 0;
    for(int i=1;i<=n;i++){
        sum+=ans[i];
        sum%=p;
    }
    cout<<sum<<endl;
    return 0;
}

E.F小红的中位数查询

https://ac.nowcoder.com/acm/contest/91177/E

https://ac.nowcoder.com/acm/contest/91177/F

1.思路

        好难,需要构造权值线段树,同时需要进行动态开点,即动态存储父亲节点和左右孩子节点的关系;同时利用主席树,即在每次插入新的元素时,都是在前一版本的权值线段树的基础之上进行节点的添加,节约时间和空间成本;查询区间l,r。利用第r版本的权值线段树-(l-1)版本的权值线段树,这样就可以将原先的求区间的第k小问题转化为求全局区间的第k小问题,此时就能利用树上二分来实现,求解问题

2.代码

官方题目讲解:https://www.bilibili.com/video/BV1EYxkerEQN?p=3&vd_source=b0cde2c7518af1c65116188091dadb6dicon-default.png?t=O83Ahttps://www.bilibili.com/video/BV1EYxkerEQN?p=3&vd_source=b0cde2c7518af1c65116188091dadb6d

线段树相关内容博客推荐:

https://blog.csdn.net/weixin_45697774/article/details/104274713icon-default.png?t=O83Ahttps://blog.csdn.net/weixin_45697774/article/details/104274713代码出处:

https://ac.nowcoder.com/acm/contest/view-submission?submissionId=71770786icon-default.png?t=O83Ahttps://ac.nowcoder.com/acm/contest/view-submission?submissionId=71770786

#include <bits/stdc++.h>
using namespace std;
int const N = 1e5 + 10;
vector<int> nums;
int w[N];       // 存储题目中的数组元素
int n, m;

struct Node{    // 结构体,用于构造线段树
    int l, r;   // l为左孩子下标,r为右孩子下标
    int cnt;    // 存储维护的线段区间的个数
}tr[24 * N];
                        // root数组用于保存节点下标,idx动态开点,用于表示当前的节点下标
int root[N], idx = 0;   // root[i]表示第i版的权值线段树的节点值

int find(int x)     // 返回x在nums中的位置下标
{
    return lower_bound(nums.begin(), nums.end(), x) - nums.begin();
}

int build(int l, int r)     // 构造空的权值线段树
{
    idx++;
    int p = idx;
    if(l == r) return p;
    int mid = (l + r) >> 1;
    tr[p].l = build(l, mid);    // 存储节点下标为p的左孩子下标
    tr[p].r = build(mid + 1, r);// 存储节点下标为p的右孩子下标
    return p;                   // 返回当前节点的下标
}

// last为前一版的线段树
int insert(int last, int l, int r, int x)   
{   
    // 1 0 3 1 idx=7
    idx++;
    int p = idx;
    tr[p] = tr[last];   // 根节点覆盖,即新建根节点
    if(l == r)
    {
        tr[p].cnt++;
        return p;
    }
    int mid = (l + r) >> 1;
    if(x <= mid) tr[p].l = insert(tr[last].l, l, mid, x);
    else tr[p].r = insert(tr[last].r, mid + 1, r, x);
    tr[p].cnt = tr[tr[p].l].cnt + tr[tr[p].r].cnt;  // 子树合并
    return p;
}

// now为当前版本的权值线段树,last为旧版权值线段树
// 这里利用:第r版本的线段树-第(l-1)版的线段树将区间第k小的问题转化为全局第k小问题来求中位数
// 树上二分来求全局第k小
int query(int now, int last, int l, int r, int k)
{

    if(l == r)  return r;
    int cnt = tr[tr[now].l].cnt - tr[tr[last].l].cnt;
    int mid = (l + r) >> 1;
    if(cnt >= k) return query(tr[now].l, tr[last].l, l, mid, k);
    else return query(tr[now].r, tr[last].r, mid + 1, r, k - cnt);
}

int main()
{
    cin >> n >> m;
    for(int i = 1;i <= n;i++)
    {
        cin >> w[i];
        nums.push_back(w[i]);
    }
    sort(nums.begin(), nums.end());
    nums.erase(unique(nums.begin(), nums.end()), nums.end());
    // 构造第0版空的权值线段树,root[0]=1,说明第0版的根节点下标为1
    root[0] = build(2, nums.size()+1);
    // 主席树的构建
    // 每插入一个新的元素,只新建对应的一边的子树的节点,未受到影响的节点利用动态开点进行连接
    for(int i = 1;i <= n;i++)
    {
        root[i] = insert(root[i - 1], 1, nums.size() , find(w[i])+1);
        // cout<<find(w[i])<<endl;
    }
    int l, r, k, ans = 0;
    while(m--)
    {
        cin >> l >> r;
        // l=1,r=3,k=2
        // l=2,r=4,k=2
        k = ((l + r) >> 1) - l + 1;
        ans = nums[query(root[r], root[l - 1], 0, nums.size() - 1, k)];
        cout << ans << endl;
    }
    return 0;
}

        

G.小红的数轴移动(二)

https://ac.nowcoder.com/acm/contest/91177/G

(0/1背包求解)(慢慢补。。)

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值