2021牛客暑期多校训练营4 E-Tree Xor(异或+思维+区间交 or Trie树)
E-Tree Xor
首先不考慮區(qū)間限制條件,我們給定其中一個點的權值后,那么其他點的權值也就確定。比如 val1=0\text{val}_1=0val1?=0,即可通過變得限制求出其他點valu\text{val}_uvalu?,而且不難發(fā)現(xiàn)如果val1=0⊕a\text{val}_1=0\oplus aval1?=0⊕a那么其余點的權值valu=0⊕a\text{val}_u=0\oplus avalu?=0⊕a
下面記valu\text{val}_uvalu?為當val1=0\text{val}_1=0val1?=0時,u點的權值。
于是問題轉化為求出滿足下面區(qū)間限制aaa的個數(shù)。
L1≤val1⊕a≤R1L2≤val2⊕a≤R2…Ln≤valn⊕a≤Rn\text L_1\leq \text{val}_1\oplus a\leq \text R_1\\\text L_2\leq \text{val}_2\oplus a\leq \text R_2\\ \dots\\ \text L_n\leq \text{val}_n\oplus a\leq \text R_n L1?≤val1?⊕a≤R1?L2?≤val2?⊕a≤R2?…Ln?≤valn?⊕a≤Rn?
考慮其中一種限制直觀的想法是Lu≤valu⊕a≤Ru?L1⊕val1≤a≤R1⊕val1\text L_u\leq \text{val}_u\oplus a\leq \text R_u\Rightarrow \text L_1\oplus \text{val}_1\ \leq a\leq \text R_1\oplus \text{val}_1Lu?≤valu?⊕a≤Ru??L1?⊕val1??≤a≤R1?⊕val1?不難發(fā)現(xiàn)上面轉化是錯誤的,而且可以發(fā)現(xiàn)aaa的區(qū)間是不連續(xù)的!!!需要找出這些不連續(xù)的區(qū)間!
于是就很難處理,后面就是講題人的做法(真滴強)
我們可以利用 [0,230?1][0,2^{30}-1][0,230?1] 的線段樹, 把 [Li,Ri][L_i , R_i][Li?,Ri?] 分成 log?W\log WlogW個連續(xù)的區(qū)間, 且每個區(qū)間的形式是 : [???00…00,???11…11][***00\dots00,***11\dots 11][???00…00,???11…11], 這樣的區(qū)間異或上 vali\text{val}_ivali? 后仍然還是一個區(qū)間
不妨設???***???的數(shù)量是k
不難發(fā)現(xiàn)區(qū)間[???00…00,???11…11][***00\dots00,***11\dots 11][???00…00,???11…11]抑或上vali\text{val}_ivali?的區(qū)間是[???00…00,???,11…11][- - -00\dots00,---,11\dots11][???00…00,???,11…11]
其中???---???是(???00…00)⊕vali(***00\dots00)\oplus \text{val}_i(???00…00)⊕vali?的前kkk位的值
比如:[10100000,10101111][\color{Blue}{1010} \color{Red}{0000},\color{Blue}{1010} \color{Red}{1111}][10100000,10101111]⊕10011010?\oplus\color{Blue}{1001} \color{Red}{1010}\Rightarrow⊕10011010? [00110000,00111111][\color{Blue}{0011} \color{Red}{0000},\color{Blue}{0011} \color{Red}{1111}][00110000,00111111]
藍色部分異或0011=1010⊕0011\color{Blue}0011=1010\oplus00110011=1010⊕0011原紅色部分不變,稍微思考一下就知道為什么了。
通過上面操作成功找出這些不連續(xù)的區(qū)間!!!
然后就sort區(qū)間差分亂搞就行。時間復雜度2log,可以用線段樹維護所有不合法區(qū)間的并集,把不合法的區(qū)間標記為1,把時間復雜度降為1log
Code1
#include<bits/stdc++.h> using namespace std; using ll=long long; template <class T=int> T rd() {T res=0;T fg=1;char ch=getchar();while(!isdigit(ch)) {if(ch=='-') fg=-1;ch=getchar();}while( isdigit(ch)) res=(res<<1)+(res<<3)+(ch^48),ch=getchar();return res*fg; } const int N=100010; int h[N],e[2*N],ne[2*N],w[2*N],idx; void add(int a,int b,int c){e[idx]=b,w[idx]=c,ne[idx]=h[a],h[a]=idx++;} int L[N],R[N]; int val[N]; struct node {int l,r; }tree[N*40]; int rt,cnt,n; vector<pair<int,int>> vec; void insert(int &u,int l,int r,int L,int R,int val) {if(!u) u=++cnt;if(L<=l&&r<=R){int ql=l^(val&(~(r-l)));//異或之后的區(qū)間[ql,qr]int qr=ql+r-l;vec.push_back({ql,1});vec.push_back({qr+1,-1});return;}int mid=l+r>>1;if(L<=mid) insert(tree[u].l,l,mid,L,R,val);if(R>mid)insert(tree[u].r,mid+1,r,L,R,val); }void dfs(int u,int fa) {insert(rt,0,(1<<30)-1,L[u],R[u],val[u]);for(int i=h[u];i!=-1;i=ne[i]){int v=e[i];if(v==fa) continue;val[v]=val[u]^w[i];dfs(v,u);} } int solve() {sort(vec.begin(),vec.end());vec.push_back({1<<30,0});int ans=0;int cur=0;for(int i=0;i<vec.size()-1;i++){cur+=vec[i].second;if(cur==n) ans+=vec[i+1].first-vec[i].first;}return ans; } int main() {n=rd();for(int i=1;i<=n;i++) L[i]=rd(),R[i]=rd();memset(h,-1,sizeof h);for(int i=1;i<n;i++){int u=rd(),v=rd(),w=rd();add(u,v,w),add(v,u,w);}dfs(1,0);printf("%d\n",solve()); }比較容易想到的做法就是我們需要求
L1≤val1⊕a≤R1L2≤val2⊕a≤R2…Ln≤valn⊕a≤Rn\text L_1\leq \text{val}_1\oplus a\leq \text R_1\\\text L_2\leq \text{val}_2\oplus a\leq \text R_2\\ \dots\\ \text L_n\leq \text{val}_n\oplus a\leq \text R_n L1?≤val1?⊕a≤R1?L2?≤val2?⊕a≤R2?…Ln?≤valn?⊕a≤Rn?
轉化成
[val1⊕a≤R1]?[val1⊕a<L1][val2⊕a≤R2]?[val2⊕a<L2]…[valn⊕a≤Rn]?[valn⊕a<Ln][\text{val}_1\oplus a\leq \text R_1]?[\text{val}_1\oplus a< \text L_1]\\ [\text{val}_2\oplus a\leq \text R_2]?[\text{val}_2\oplus a< \text L_2]\\ \dots\\ [\text{val}_n\oplus a\leq \text R_n]?[\text{val}_n\oplus a< \text L_n] [val1?⊕a≤R1?]?[val1?⊕a<L1?][val2?⊕a≤R2?]?[val2?⊕a<L2?]…[valn?⊕a≤Rn?]?[valn?⊕a<Ln?]
上面的?理解為兩個集合相減。
實際上我需要求的就是⊕a≤b\oplus a\leq b⊕a≤b的區(qū)間,顯然字典樹可以做,相當于求⊕a≤b\oplus a\leq b⊕a≤b的數(shù)有哪些,和第二次杭電I love counting求的步驟一樣,在Trie樹上討論一下就行。
Code2
#include<bits/stdc++.h> using namespace std; using ll=long long; template <class T=int> T rd() {T res=0;T fg=1;char ch=getchar();while(!isdigit(ch)) {if(ch=='-') fg=-1;ch=getchar();}while( isdigit(ch)) res=(res<<1)+(res<<3)+(ch^48),ch=getchar();return res*fg; } const int N=100010; int h[N],e[2*N],ne[2*N],w[2*N],idx; void add(int a,int b,int c){e[idx]=b,w[idx]=c,ne[idx]=h[a],h[a]=idx++;} int L[N],R[N]; int val[N]; struct node {int l,r; }tree[N*40]; int rt,cnt,n; vector<pair<int,int>> seg[2]; void query(int k,int a,int b)// x^a<=b {int u=rt;int cur=0;for(int i=29;i>=0;i--)//30位{int ai=a>>i&1;int bi=b>>i&1;if(bi==1) //b=1{if(ai==0) {seg[k].push_back({cur,cur+(1<<i)-1});cur+=1<<i;if(!tree[u].r) tree[u].r=++cnt;u=tree[u].r;}else{seg[k].push_back({cur+(1<<i),cur+(1<<i)+(1<<i)-1});if(!tree[u].l) tree[u].l=++cnt;u=tree[u].l;}}else{if(ai==0){if(!tree[u].l) tree[u].l=++cnt;u=tree[u].l;}else {cur+=1<<i;if(!tree[u].r) tree[u].r=++cnt;u=tree[u].r;}}}seg[k].push_back({cur,cur}); } void dfs(int u,int fa) {for(int i=h[u];i!=-1;i=ne[i]){int v=e[i];if(v==fa) continue;val[v]=val[u]^w[i];dfs(v,u);} } int solve() {vector<pair<int,int>>vec;for(auto t:seg[0]) {vec.push_back({t.first,-1});vec.push_back({t.second+1,+1});}for(auto t:seg[1]) {vec.push_back({t.first,+1});vec.push_back({t.second+1,-1});}sort(vec.begin(),vec.end());vec.push_back({1<<30,0});int ans=0;int cur=0;for(int i=0;i<vec.size()-1;i++){cur+=vec[i].second;if(cur==n) ans+=vec[i+1].first-vec[i].first;}return ans; } int main() {n=rd();for(int i=1;i<=n;i++) L[i]=rd(),R[i]=rd();memset(h,-1,sizeof h);for(int i=1;i<n;i++){int u=rd(),v=rd(),w=rd();add(u,v,w),add(v,u,w);}dfs(1,0);for(int i=1;i<=n;i++) {if(L[i]>0) query(0,val[i],L[i]-1);query(1,val[i],R[i]);}printf("%d\n",solve()); }其實仔細分析應該能寫出Trie樹的做法,不過當時沒有分析出來啊www
要加油哦~
總結
以上是生活随笔為你收集整理的2021牛客暑期多校训练营4 E-Tree Xor(异或+思维+区间交 or Trie树)的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 只缘身在此山中是什么意思 题西林壁原文及
- 下一篇: 2021牛客暑期多校训练营4 D-Reb