牛客第七场 题解

本文通过三道题目展示了位运算在解决签到题中的应用,基本题中数组元素三元组的关系计算,以及进阶题中涉及树结构的问题。通过解析代码,阐述了如何利用位运算快速求解满足特定条件的正整数数量,枚举与树状数组结合解决数组元素关系问题,以及使用DFS和主席树解决树结构的最大子集问题。
摘要由CSDN通过智能技术生成

比赛传送门
作者: fn


签到题

I题 xay loves or xay喜欢或运算

题目大意
给定 x x x s s s ,计算有多少正整数 y y y 满足 x or ⁡ y = s x \operatorname{or} y=s xory=s

考察内容
位运算

分析

对s和x上的每一个二进制位 ,

  • x上为0,不影响答案;
  • x上为1且s上为0,无解;
  • x上和s上都为1,则答案乘二。

特判:x=s时会计算y=0的情况,这时候答案要减一。

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

int main(){ 
	ios::sync_with_stdio(0); cin.tie(0);
	ll x,s,ans=1,c=0;
	cin>>x>>s;
	if(x==s) c=1;
	while(x || s){
		if(x&1){
			if((s&1)==0){
				cout<<"0"<<endl;
				return 0;
			}
			else{
				ans<<=1;
			}
		}
		x>>=1;
		s>>=1;
	}
	cout<<ans-c<<endl;
	return 0;
}

基本题

H题 xay loves count xay爱数数

题目大意
给定一个长度为 n n n 的数组 a a a ,计算有多少个三元组 ( i , j , k ) (i, j, k) (i,j,k) 满足 a i ∗ a j = a k a_i * a_j = a_k aiaj=ak

考察内容
枚举

分析
枚举 a k a_k ak ,然后再遍历 1 , 2 , 3 , . . . , a k 1 ,2, 3, ... , \sqrt{a_k} 1,2,3,...,ak a i a_i ai 就能确定 a j a_j aj

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

int n,a[1000005],t[1000005],qk,nt[1000005];
long long ans;
int main(){ // AC
	scanf("%d",&n);
	for(int i=1;i<=n;i++) {
		scanf("%d",&a[i]);
		t[a[i]]++;
	}
	for(int i=1,l=0;i<=1000000;i++) {
		if(t[i]>0) {
			nt[l]=i;
			l=i;
		}
	}
	for(int k=1;k<=n;k++) {
		qk=sqrt(a[k]);
		for(int i=nt[0],j;i!=0&&i<=qk;i=nt[i]) {
			j=a[k]/i;
			if(a[k]==j*i&&t[j]>0) {
				if(i==j) ans+=t[i]*t[j];
				else ans+=t[i]*t[j]*2;
			}
		}
	}
	printf("%lld",ans);
    return 0;
}

进阶题

F题 xay loves trees xay喜欢树

题目大意
有两棵根结点为1的树,都有 n n n 个节点。 你需要找到 { 1 , 2 , ⋯   , n } \{1,2, \cdots, n\} {1,2,,n} 的最大子集,使:

  1. 在第一棵树上,集合是连通的,对于集合中的任意两点 u u u , v v v ,其中一个 u u u v v v 是另一个的祖先。
  2. 在第二棵树中,集合中的任意两点都不是对方的祖先。

输出集合中的元素个数的最大值。

考察内容
树状数组,倍增,dfs,主席树(可持久化线段树)

分析
在这里插入图片描述
在这里插入图片描述

#include<bits/stdc++.h>
using namespace std;
 
const int N=3e5+1e3+7;
 
int T,n;
 
vector<int>g[N],h[N];
 
int dc,st[N],ed[N],rev[N];
 
void dfs(int x,int f)
{
	st[x]=++dc;
	rev[dc]=x;
	for(auto v:h[x])
	{
		if(v==f)
			continue;
		dfs(v,x);
	}
	ed[x]=dc;
}
 
set<int>s;
 
vector<int>del[N];

int ans,last;

int to[N];
 
void calc(int x,int f)
{
	int lst=last;
	while(s.size())
	{
		int ok=1;
		auto it=s.lower_bound(st[x]);
		if(it!=s.end()&&*it<=ed[x])
			ok=0;
		else if(it!=s.begin())
		{
			it--;
			if(*it<=st[x]&&st[x]<=ed[rev[*it]])
				ok=0;
		}
		if(ok)
			break;
		s.erase(st[last]);
		del[x].push_back(st[last]);
		last=to[last];
	}
	s.insert(st[x]);
	ans=max(ans,(int)s.size());
	for(auto v:g[x])
	{
		if(v==f)	
			continue;
		to[x]=v;
		calc(v,x);
	}
	s.erase(st[x]);
	for(auto p:del[x])
		s.insert(p);
	last=lst;
}
 
int main()
{
	scanf("%d",&T);
	while(T--)
	{
		scanf("%d",&n);
		for(int i=1;i<=n;i++)
			g[i].clear(),h[i].clear(),del[i].clear();
		ans=0;
		for(int i=1;i<n;i++)
		{
			int u,v;
			scanf("%d%d",&u,&v);
			g[u].push_back(v);
			g[v].push_back(u);
		}
		for(int i=1;i<n;i++)
		{
			int u,v;
			scanf("%d%d",&u,&v);
			h[u].push_back(v);
			h[v].push_back(u);
		}
		last=1;
		dc=0;
		dfs(1,0);
		s.clear();
		calc(1,0);
		printf("%d\n",ans);
	}
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值