牛客第九场 题解

比赛传送门
作者: fn


签到题

H题 Happy Number / 快乐数

题目大意
求第n个只包含2,3,6的正整数。

考察内容

分析
• 首先需要确定出 k 大数有多少位
• 显然1位的、2位的…分别有3, 3 2 3^2 32 ,⋯ 个
• 于是我们知道了 k 大数是 x 位下第 k’大的
• 然后我们可以把 2 看做 0,3 看做 1 ,6 看做 2
• 问题就转换成了三进制数转换

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

const int num[4]={6,2,3,0};
ll n,m,t=1;
int x[40];

int main(){ 
	ios::sync_with_stdio(0); cin.tie(0);
	cin>>n;
	
	for(int i=3;n-i>0;i*=3){
		n-=i;
		t++;
	}
	
	for(int i=t;i>=1;i--){
		x[i]=num[n%3];
		n=(n+2)/3; // 向上取整 
	}
	for(int i=1;i<=t;i++) cout<<x[i];
}

进阶题

I题 Incentive Model / 激励模型

题目大意
矿工A、B在挖矿,给定 a a a ,挖矿概率 p A = a 2 256 p_A=\frac{ a}{2^{256}} pA=2256a p B = 1 − a 2 256 p_B=\frac{1-a}{2^{256}} pB=22561a,挖到矿的人挖下一块矿的概率提升 w w w ,求挖到 n n n 块区块后A拥有区块数的预期。

考察内容
概率与数学期望,猜结论,乘法逆元

结论
答案是 n ∗ a n*a na

#include<bits/stdc++.h>
#define ll long long
using namespace std;
const int mo=998244353;
ll n,w,x,y;

ll ksm(ll a,ll b)
{
	ll ans=1;
	for(;b;a=a*a%mo,b>>=1)if(b&1)
		ans=ans*a%mo;
	return ans;
}

int main(){ 
	ios::sync_with_stdio(0); cin.tie(0);
	cin>>n>>w>>x>>y;
	cout<<n*x%mo*ksm(y,mo-2)%mo<<endl;
}

E题 Eyjafjalla / 艾雅法拉冰川火山

题目大意
给定一个以 1 为根的树,孩子的点权小于父亲的点权。 q q q 次询问,每次询问包含 x x x 节点的权值范围为 [ l , r ] [l, r] [l,r] 的极大连通块的大小

考察内容
倍增,可持久化线段树

分析
• 每次询问可以看作两个阶段,第一阶段先往上搜索能到达的最高的节点 p,第二阶段查询 p 的子树中所有点权大于 l l l 的节点个数。

• 第一阶段可以通过倍增法求得 p p p
• 第二阶段相当于在 p p p 的子树中查询权值大于 l l l 的节点个数,根据每个节点的 dfs 序建立可持久化线段树。先把 q q q 次询问按 l l l 的大小排序(离线),然后在线段树上查询。

• 时间复杂度 O ( n l o g n ) O(n log n) O(nlogn)

#include<bits/stdc++.h>
using namespace std;
#define MAX 100009
#define cer(x) cerr<<(#x)<<" = "<<(x)<<'\n'

int n,q;
vector<int> E[MAX];
int t[MAX],ord[MAX];
int fa[19][MAX];
int fir[MAX],sec[MAX];
void DFS(int u){
	fir[u]=++fir[0];
	for(int j=1;j<=17;++j) fa[j][u]=fa[j-1][fa[j-1][u]];
	for(int v:E[u]){
		if(v==fa[0][u]) continue;
		fa[0][v]=u;
		DFS(v);
	}
	sec[u]=fir[0];
}

int ans[MAX];
struct query{
	int x,l,r,id;
	inline void Get(int i){
		scanf("%d%d%d",&x,&l,&r);
		id=i;
	}
	bool operator <(const query& A) const {return l>A.l;}
}Q[MAX];

int sum[MAX];
inline void add(int x){
	for(int i=x;i<=n;i+=(i&-i)) sum[i]++;
}
inline int Query(int x){
	int ans=0;
	for(int i=x;i>=1;i-=(i&-i)) ans+=sum[i];
	return ans;
}

signed main(){
	scanf("%d",&n);
	for(int i=1;i<n;++i){
		int x,y;scanf("%d%d",&x,&y);
		E[x].push_back(y);
		E[y].push_back(x);
	}
	for(int i=1;i<=n;++i){
		scanf("%d",t+i);
	}
	DFS(1);
	scanf("%d",&q);
	for(int i=1;i<=q;++i) Q[i].Get(i);
	for(int i=1;i<=n;++i) ord[i]=i;
	sort(ord+1,ord+n+1,[](int a,int b){return t[a]>t[b];});
	sort(Q+1,Q+q+1);
	for(int i=1,j=1;i<=q;++i){
		while(j<=n&&t[ord[j]]>=Q[i].l) add(fir[ord[j++]]);
		int u=Q[i].x;
		if(Q[i].r<t[u]||Q[i].l>t[u]) {ans[Q[i].id]=0;continue;}
		for(int k=17;k>=0;--k){
			if(fa[k][u]&&t[fa[k][u]]<=Q[i].r) u=fa[k][u];
		}
		ans[Q[i].id]=Query(sec[u])-Query(fir[u]-1);
	}
	for(int i=1;i<=q;++i) cout<<ans[i]<<'\n';
}

  • 2
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值