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.代码
线段树相关内容博客推荐:
#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背包求解)(慢慢补。。)