2021牛客暑期多校训练营9,签到题HE

Posted 小哈里

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了2021牛客暑期多校训练营9,签到题HE相关的知识,希望对你有一定的参考价值。

题号 标题 已通过代码 通过率 团队的状态
A A Math Challenge 点击查看 34/412 未通过
B Best Subgraph 点击查看 10/77 未通过
C Cells 点击查看 100/283 未通过
D Divide-and-conquer on Tree 点击查看 8/95 未通过
E Eyjafjalla 点击查看 652/3232 未通过(倍增,dfs序,主席树)
F Financial Order Execution 点击查看 2/125 未通过
G Glass Balls 点击查看 65/135 未通过
H Happy Number 点击查看 1489/2496 通过(签到)
I Incentive Model 点击查看 197/546 未通过
J Jam 点击查看 94/1106 未通过

H Happy Number

题意:

  • 一个数字是快乐的当且仅当其在10进制下只由236组成,求第n个快乐数,n<1e9。

思路:

  • 首先容易想到3进制,但是直接进制转换不能通过,找了一下规律发现,
    0-2,1-3,2-6,00-22,01-23,02-26,10-32,11-32,12-33,,000102在三进制里明显是不合法的,因为和012重复了,但是快乐数里确实合法的。所以在进制转换的时候稍微改一下。
#include<bits/stdc++.h>
using namespace std;
string p="236";
int main(){
	int n;  cin>>n;
	string s="";
	while(n>0){
		n--;
		s = p[n%3]+s;
		n /= 3;
	}
	cout<<s;
	return 0;
}

E Eyjafjalla

题意:

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

思路:

  • 病毒传播可以看作两个阶段,第一个阶段先上升到最高的一个节点p(p的温度大于r),第二阶段感染p的子树中所有温度大于l的城市。
  • 第一阶段可以通过倍增法求得p
  • 第二阶段相当于在p的子树中查询权值大于l的节点个数,根据每个节点的dfs序建立可持久化线段树, 然后在线段树上查询即可。时间复杂度O(n log n)。
#include<bits/stdc++.h>
using namespace std;
const int maxn  = 1e5+10;

int n;
vector<int>G[maxn];
int a[maxn];

//倍增找p
int fa[maxn], up[maxn][30];
void getf(){
	for(int i = 1; i <= n; i++)up[i][0]=fa[i];
	for(int j = 1; j <= 20; j++)
		for(int i = 1; i <= n; i++)
			up[i][j] = up[up[i][j-1]][j-1];//i向上2^j层的祖先
}

//预处理dfs序[dfn,low]
int clk, dfn[maxn], low[maxn], ord[maxn];
void pre(int x, int f){
	fa[x] = f;
	dfn[x] = ++clk;
	ord[clk] = x;
	for(int to: G[x]){
		if(to != f)pre(to,x);
	}
	low[x] = clk;
}

//主席树
int tot, rt[maxn<<5], lch[maxn<<5], rch[maxn<<5], sum[maxn<<5];
void modify(int x, int f, int l, int r, int pos){  //v[pos]+=1;
	if(l==r){
		sum[x] = sum[f]+1;
		return ;
	}
	int mid = l+r>>1;
	if(pos <= mid){
		rch[x] = rch[f];//直接用上一棵树的节点
		lch[x] = ++tot; //需要新建节点
		modify(lch[x], lch[f], l, mid, pos);
	}else{
		lch[x] = lch[f];
		rch[x] = ++tot;
		modify(rch[x], rch[f], mid+1,r,pos);
	}
	sum[x] = sum[lch[x]]+sum[rch[x]];
}
int query(int p, int l, int r, int ll, int rr){//查询版本线段树值域范围在[l,r]的节点个数
	if(p==0)return 0;
	if(ll<=l && r<=rr)return sum[p];
	if(l>rr || r<ll)return 0;
	int mid = (l+r)>>1, ans = 0;
	ans += query(lch[p], l, mid, ll, rr);
	ans += query(rch[p], mid+1, r, ll, rr);
	return ans;
}


int main(){
	ios::sync_with_stdio(0),cin.tie(0),cout.tie(0);
	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);
	}
	for(int i = 1; i <= n; i++)cin>>a[i];  a[0] = 1e9+7;
	pre(1,0);
	getf();
	for(int i = 1; i <= n; i++){//建立n个版本的值域线段树
		rt[i] = ++tot;
		modify(rt[i], rt[i-1], 1, 1e9, a[ord[i]]);
	}
	int q;  cin>>q;
	while(q--){
		int x, l, r;  cin>>x>>l>>r;
		if(a[x]<l || a[x]>r){ cout<<"0\\n"; continue; }
		for(int i = 20; i >= 0; i--){//先上升到最高点,满足刚好a[x]>r
			if(a[up[x][i]]<=r)x=up[x][i];
		}
		//查询子树[dfn[x]-1, low[x]]中,权值在[l,r]中的节点个数
		cout<<query(rt[low[x]], 1, 1e9, l,r)-query(rt[dfn[x]-1], 1,1e9, l, r)<<"\\n";
	}
	return 0;
}

以上是关于2021牛客暑期多校训练营9,签到题HE的主要内容,如果未能解决你的问题,请参考以下文章

2021牛客暑期多校训练营1, 签到题DFBG

2021牛客暑期多校训练营2,签到题CDFKI

“蔚来杯“2022牛客暑期多校训练营9,签到题ABGIE

2021牛客暑期多校训练营7,签到题FHI

2021牛客暑期多校训练营6,签到题CFHI

2021牛客暑期多校训练营10,签到题FH