生活随笔
收集整理的這篇文章主要介紹了
2021牛客暑期多校训练营4 E - Tree Xor 线段树 + 拆分区间
小編覺得挺不錯的,現在分享給大家,幫大家做個參考.
傳送門
文章目錄
題意:
給你一棵樹,每個點原本都有一個權值wiw_iwi?,但是你只知道相鄰兩個點之間的wu⊕wvw_u\oplus w_vwu?⊕wv?,問你有多少種w1,2,...,nw_{1,2,...,n}w1,2,...,n?
n≤1e5,wi<230n\le1e5,w_i<2^{30}n≤1e5,wi?<230
思路:
顯然我們如果確定了一個點的權值,那么其他的點都就確定了。
更具體的是,我們可以求出111到其他點之間的w1⊕wxw_1\oplus w_xw1?⊕wx?,其中我們可以假設w1=0w_1=0w1?=0,那么當給w1⊕aw_1\oplus aw1?⊕a時,就相當于將其他w2,3,...,n⊕aw_{2,3,...,n}\oplus aw2,3,...,n?⊕a,所以我們問題就轉換成了對于每個區間求合法的aaa使得li≤wi⊕a≤ril_i\le w_i\oplus a\le r_ili?≤wi?⊕a≤ri?,轉換一下就是wi⊕[li,ri]w_i\oplus [l_i,r_i]wi?⊕[li?,ri?]的合法區間。這個區間肯定不是連續的,所以考慮將其拆分成若干區間。
由于拆分的區間肯定不能很多,所以考慮用二進制來拆分這個區間。考慮這樣一種形式的二進制區間[xxxx0000,xxxx1111][xxxx0000,xxxx1111][xxxx0000,xxxx1111],其中xxx表示可以是任意數,但是兩個的xxx對應位置必須相同。這樣的區間滿足其異或上wiw_iwi?仍是一段連續的區間。我們驚奇的發現,這個區間后半部分不正是線段樹的每段區間嗎?所以考慮建一棵[0,230?1][0,2^{30}-1][0,230?1]的線段樹,將這個區間插入,可知最多能被分成lognlognlogn段區間。當這個區間被完全包含的時候,我們打一個懶標記即可。
查詢的時候如果懶標記=n=n=n的時候,就直接返回區間長度即可。
#include<cstdio>
#include<iostream>
#include<string>
#include<cstring>
#include<map>
#include<cmath>
#include<cctype>
#include<vector>
#include<set>
#include<queue>
#include<algorithm>
#include<sstream>
#include<ctime>
#include<cstdlib>
#include<random>
#include<cassert>
#define X first
#define Y second
#define L (u<<1)
#define R (u<<1|1)
#define pb push_back
#define mk make_pair
#define Mid ((tr[u].l+tr[u].r)>>1)
#define Len(u) (tr[u].r-tr[u].l+1)
#define random(a,b) ((a)+rand()%((b)-(a)+1))
#define db puts("---")
using namespace std
;
typedef long long LL
;
typedef unsigned long long ULL
;
typedef pair
<int,int> PII
;const int N
=5000010,mod
=1e9+7,INF
=0x3f3f3f3f;
const double eps
=1e-6;int n
,w
[N
],p
[N
];
int l
[N
],r
[N
],idx
;
struct Node {int l
,r
;int cnt
,lazy
;
}tr
[N
<<2];
vector
<PII
>v
[N
];void dfs(int u
,int fa
) {for(auto x
:v
[u
]) {if(x
.X
==fa
) continue;w
[x
.X
]=w
[u
]^x
.Y
;dfs(x
.X
,u
);}
}int newnode() {int u
=++idx
;tr
[u
].l
=tr
[u
].r
=0;tr
[u
].cnt
=tr
[u
].lazy
=0;return u
;
}void insert(int &u
,int l
,int r
,int ql
,int qr
,int w
,int dep
) {if(!u
) u
=newnode();int nl
=(l
^w
)&(((1<<30)-1)^((1<<(dep
+1))-1));int nr
=(l
^w
)|((1<<(dep
+1))-1);if(nl
>=ql
&&nr
<=qr
) {tr
[u
].lazy
++;tr
[u
].cnt
++;return;}if(tr
[u
].cnt
) {if(!tr
[u
].l
) tr
[u
].l
=newnode();if(!tr
[u
].r
) tr
[u
].r
=newnode();tr
[tr
[u
].l
].cnt
+=tr
[u
].cnt
; tr
[tr
[u
].r
].cnt
+=tr
[u
].cnt
;tr
[u
].cnt
=0;}int mid
=(l
+r
)>>1;int ll
=(l
^w
)&(((1<<30)-1)^((1<<(dep
))-1)),lr
=(l
^w
)|((1<<(dep
))-1);int rl
=(r
^w
)&(((1<<30)-1)^((1<<(dep
))-1)),rr
=(r
^w
)|((1<<(dep
))-1);if(w
>>dep
&1) {if(ql
<=rr
) insert(tr
[u
].r
,mid
+1,r
,ql
,qr
,w
,dep
-1);if(qr
>=ll
) insert(tr
[u
].l
,l
,mid
,ql
,qr
,w
,dep
-1);} else {if(ql
<=lr
) insert(tr
[u
].l
,l
,mid
,ql
,qr
,w
,dep
-1);if(qr
>=rl
) insert(tr
[u
].r
,mid
+1,r
,ql
,qr
,w
,dep
-1);}
}int query(int u
,int l
,int r
) {if(!u
) return 0;if(tr
[u
].cnt
==n
) return r
-l
+1;if(tr
[u
].cnt
) {if(tr
[u
].l
) tr
[tr
[u
].l
].cnt
+=tr
[u
].cnt
;if(tr
[u
].r
) tr
[tr
[u
].r
].cnt
+=tr
[u
].cnt
;tr
[u
].cnt
=0;}int ans
=0,mid
=(l
+r
)>>1;ans
+=query(tr
[u
].l
,l
,mid
);ans
+=query(tr
[u
].r
,mid
+1,r
);return ans
;
}int main()
{
scanf("%d",&n
);for(int i
=1;i
<=n
;i
++) scanf("%d%d",&l
[i
],&r
[i
]);for(int i
=1;i
<=n
-1;i
++) {int a
,b
,c
; scanf("%d%d%d",&a
,&b
,&c
);v
[a
].pb({b
,c
}); v
[b
].pb({a
,c
});}dfs(1,0); int rt
=newnode();int limit
=(1<<30)-1;for(int i
=1;i
<=n
;i
++) {insert(rt
,0,limit
,l
[i
],r
[i
],w
[i
],29);}printf("%d\n",query(1,0,limit
));return 0;
}
總結
以上是生活随笔為你收集整理的2021牛客暑期多校训练营4 E - Tree Xor 线段树 + 拆分区间的全部內容,希望文章能夠幫你解決所遇到的問題。
如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。