2021暑假牛客多校第四场补题:

E题 Tree Xor:

Tree Xor

题目描述:

给定一棵树和每条边的边权 对每个点给定一个范围[l,r]
边权定义为两个点的值的异或
求有多少个点值满足对于所有的点 点值属于区间[l,r]

题解:

确定一个点的值则其余所有点的值都确定下来
先考虑一个简单的问题 如果边权是两个点的差值(假设编号大的点的点值大)
那么要求多少个点就相当于将每个区间或减或加然后对所有区间求交集
比如现在的树是一条链1-2-3-4 边权依次为 1 1 2 区间依次为[3,4] ,[2,5], [1,6], [0,7] 那么就只需要求[3,4],[1,4],[-1,4],[-4,3]四个区间的交集
本题唯一的不同是边权变为异或 那么只需要求每个区间异或一个值之后的交集
但是异或之后点值不连续 所以将区间拆解成异或连续的多个区间 然后对多个区间求交集
拆解的方法很简单 比如01001 要得到所有比这个数大的区间 那么拆解成如下区间
[10000,11111] ,[01100,01111],[01010,01011]即遇到0则置为1然后二进制穷举低位。对左右端点各自拆解 然后取所有区间的交集即可
拆完之后再与给定值异或 然后将最多n*60个连续区间求交集即可
时间复杂度为O(nlog^2)

#define pll pair<ll,ll>
#define mp make_pair
using namespace std;
typedef long long ll;
const int INF=(1ll<<31)-1;
const int N=1e5+5;
vector<pair<int,int>> range;
vector<pair<int,int>> G[N];
int bit[40];
stack<int> s;
struct node{
	int l;
	int r;
}p[N];
void print(int x) {
	int t=x;
	while(t) {
		if(t&1)s.push(1);
		else s.push(0);
		t>>=1;
	}
	while(s.size()) {
		cout<<s.top();
		s.pop();
	}
	cout<<endl;
}
void getrange(ll x,int i) {
	ll l=x&bit[i],r=l+(1<<i)-1;
	range.push_back(mp(l,0));
	range.push_back(mp(r,1));
}
void init() {
	bit[0]=INF;
	for(int i=0; i<=29; i++) { //biti表示将i-1到0位的值清0
		bit[i+1]=bit[i]-(1<<i);
	}
}

void dfs(int u,int fa,int k){
	int l,r;
	for(int i=30;i>=0;i--){
		if(((p[u].r>>i)&1)==0){
			r=p[u].r+(1<<i);
			getrange(r^k,i);
		}
		if(((p[u].l>>i)&1)==1){
			l=p[u].l-(1<<i);
			getrange(l^k,i);
		}
	}
	for(auto ne:G[u]){
		if(ne.first==fa)continue;
		dfs(ne.first,u,k^ne.second);
	}
}
int main() {
	int n;
	cin>>n;
	init();
//	int i=31;
//	while(i--){if(((n>>i)&1)==0)getrange(n+(1<<i),i);}
//	for(auto v:range){
//		cout<<v.second<<endl;
//		print(v.first);
//	}
	for(int i=1;i<=n;i++){
		scanf("%d%d",&p[i].l,&p[i].r);
	}
	for(int i=1;i<n;i++){
		int u,v,w;scanf("%d%d%d",&u,&v,&w);
		G[u].push_back(mp(v,w));
		G[v].push_back(mp(u,w));
	}
	dfs(1,0,0);
	sort(range.begin(),range.end());
	int ans=0,cnt=0;
	if(range.size())ans=max(range[0].first-0-1,0);
	for(int i=0;i<range.size();i++){
		if(range[i].second==0)cnt++;
		else cnt--;
		if(i+1<range.size()&&cnt==0){
			ans+=max(range[i+1].first-range[i].first-1,0);
		}
	}
//	ans+=max(-range[range.size()-1].first-1,0);
	cout<<ans<<endl;
	return 0;
}
/*
4
3 10
3 10
3 10
3 10
1 2 2
1 3 3
2 4 4
ans:1
*/	

为了方便代码是拆l-r区间之外的然后取并
持续更新

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值