线段树 ---- 牛客多校4 ETree Xor 区间异或分段

本文介绍了一种解决特定树形结构中节点权值问题的方法,通过设定初始节点权值为0,利用异或运算推导出其他节点的权值范围,并通过区间划分和线段树思想来计算满足条件的权值数量。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

题目链接


题目大意:

就是给你 n n n个节点的树,树上每个节点都有一个权值 w i ∈ [ l i , r i ] w_i\in[l_i,r_i] wi[li,ri],以及相邻 ( u , v ) (u,v) (u,v)的异或值 w u X O R w v w_uXORw_v wuXORwv
问你 w 1... n w_{1...n} w1...n有多少种取值?


解题思路:

  1. 我们知道如果一个节点的权值定下来的话,那么整颗树的所有点的权值都定下来了!

  2. 现在我们假设 w 1 ′ = 0 w'_1=0 w1=0那么我们就可以解出 w 2.... n ′ w'_{2....n} w2....n我们设这个为初始解

  3. 如果 w 1 ′ = a w'_1=a w1=a的话就相当与 w 2... n ′ X O R a w'_{2...n}XORa w2...nXORa

  4. 那么就是有 n n n组限制
    l i ≤ w i X O R a ≤ r i ∣ i ∈ [ 1 , n ] l_i\leq w_iXORa\leq r_i|i\in[1,n] liwiXORarii[1,n]
    求满足上面式子 a a a的个数
    那么
    w i X O R l i ≤ a ≤ w i X O R r i ∣ i ∈ [ 1 , n ] w_iXORl_i\leq a \leq w_iXORr_i|i\in[1,n] wiXORliawiXORrii[1,n]
    但是根据异或的性质这些区间是不连续的
    那么就是求
    [ w i ( X O R ) l i , w i ( X O R ) r i ] ∣ i ∈ [ 1 , n ] [w_i(XOR)l_i , w_i(XOR)r_i]|i\in[1,n] [wi(XOR)li,wi(XOR)ri]i[1,n]
    里面连续的区间
    然后对这 n n n组区间求交就是答案了!!

  5. 但是如何求呢?
    我们知道对于 [ 8 , 15 ] 10 [8,15]_{10} [8,15]10 − > -> > [ 1000 , 1111 ] 2 [1000,1111]_2 [1000,1111]2
    里面除了高位之外下面的低3位的组合是全部的组合!
    就是 000 , 001 , 010 , . . . . 110 , 111 000,001,010,....110,111 000,001,010,....110,111都是有的,那么就是你任何一个数异或上这个区间里面的数,你低3位的取值是全部的!那么你只看高位,低3位全部变成0,就是这个区间里面的最小值,然后低3位全是1就是最大值!!

那么这个区间怎么划分呢?
巧了和线段树有关
[ 0 , 1 , 2 , 3 , 4 , 5 , 6 , 7 ] 10 [0,1,2,3,4,5,6,7]_{10} [0,1,2,3,4,5,6,7]10
[ 000 , 001 , . . . . , 110 , 111 ] 2 [000,001,....,110,111]_2 [000,001,....,110,111]2
那么区间划分除2
[ 0 , 1 , 2 , 3 ] [ 4 , 5 , 6 , 7 ] [0,1,2,3] [4,5,6,7] [0,1,2,3][4,5,6,7]
[ 000 , 001 , 010 , 011 ] 2 [ 100 , 101 , 110 , 111 ] 2 [000,001,010,011]_2[100,101,110,111]_2 [000,001,010,011]2[100,101,110,111]2这时候就是低2位了!!
每次划分一次低位就减一!!

#include <bits/stdc++.h>
#define mid ((l + r) >> 1)
#define Lson rt << 1, l , mid
#define Rson rt << 1|1, mid + 1, r
#define ms(a,al) memset(a,al,sizeof(a))
#define log2(a) log(a)/log(2)
#define lowbit(x) ((-x) & x)
#define IOS std::ios::sync_with_stdio(0); cin.tie(0); cout.tie(0)
#define INF 0x3f3f3f3f
#define LLF 0x3f3f3f3f3f3f3f3f
#define f first
#define s second
#define endl '\n'
using namespace std;
const int N = 2e6 + 10, mod = 1e9 + 9;
const int maxn = 500010;
const long double eps = 1e-5;
const int EPS = 500 * 500;
typedef long long ll;
typedef unsigned long long ull;
typedef pair<int,int> PII;
typedef pair<ll,ll> PLL;
typedef pair<double,double> PDD;
template<typename T> void read(T &x)
{
   x = 0;char ch = getchar();ll f = 1;
   while(!isdigit(ch)){if(ch == '-')f*=-1;ch=getchar();}
   while(isdigit(ch)){x = x*10+ch-48;ch=getchar();}x*=f;
}
template<typename T, typename... Args> void read(T &first, Args& ... args) 
{
   read(first);
   read(args...);
}
int n;
int L[maxn], R[maxn];
vector<PII> G[maxn];
int val[maxn];
vector<PII> vec;
inline void dfs(int u, int fa, int Xor) {
   val[u] = Xor;
   for(auto it : G[u]) {
      int v = it.first, w = it.second;
      if(v == fa) continue;
      dfs(v,u,Xor^w);
   }
}

inline void query(int L, int R, int val, int l, int r, int pos) {
   if(L <= l && R >= r) {//把低pos位设置成0
      int ql = (l ^ val) & (((1 << 30) - 1) ^ ((1 << pos) - 1));
      int qr = ql + (1 << pos) - 1;//设置成1
      vec.push_back({ql,qr});
      return;
   } 
   if(L <= mid) query(L,R,val,l,mid,pos-1);
   if(R > mid) query(L,R,val,mid+1,r,pos-1);
}

inline int slove() {//差分求区间交!!
   vector<PII> res;
   for(auto it : vec) {
      res.push_back({it.first,1});
      res.push_back({it.second+1,-1});
   }
   sort(res.begin(),res.end());
   int tag = 0;
   int ans = 0;
   int last;
   for(auto it : res) {
      if(tag == n) ans += it.first - last;
      tag += it.second;
      last = it.first;
   }
   return ans;
}

int main() {
    IOS;
    int T;
   //  cin >> T;
    T = 1;
    while(T --) {
       cin >> n;
       for(int i = 1; i <= n; ++ i) cin >> L[i] >> R[i];
       for(int i = 1; i <= n - 1; ++ i) {
          int u, v , val;
          cin >> u >> v >> val;
          G[u].push_back({v,val});
          G[v].push_back({u,val});
       }
       dfs(1,-1,0);
       for(int i = 1; i <= n; ++ i) 
          query(L[i],R[i],val[i],0,(1 << 30)-1,30);
       
       cout << slove();
    }
}
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值