E题 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区间之外的然后取并
持续更新