求给定权重范围,求树的可能存在种数的问题

原题链接
给出n个点的一棵树,每个点的权值范围,以及n-1条边和这条边的两个节点的权值异或值。求这棵树的可能存在种数。
分析:
由异或性质:A ^ B=C,则A ^ B ^ B=A=C ^ B。
以1为根节点,若val[1]=0,则可以直接dfs求出其他节点的值val[i]。若val[1]=a,易知val’[i]=val[i]^a。
因此,求这棵树的可能存在种数,即是求a的可能种数。
要使l[i]<=val’[i]<=r[i],即l[i]<=val[i]^a<=r[i],故a的种数即是满足 l[i]<=val[i]^a<=r[i]的a的所有种数。
要求l[i]<=val[i]^a<=r[i],不如先求 1<=val[i]^a<=l[i]-1, 并将此时a(初始范围为1至正无穷)的可能存在范围标记-1,1<=val[i]^a<=r[i],并将此时a的可能存在范围标记1。这样,范围里值为1的就是符合此val[i]对应的a的范围。最终范围里值为n的就是a的总范围。
枚举x=val[i]^a的值,可以用dfs加二进制法,例如对于 1<=val[i]^a<=r,此时遍历到j位,则r和val[i]的j位分别可能是11、10、01、00。记录此时a的值已经是nowA。
11:此时若a的j位为1,则此时x的j位为1^1=0,故此后x必定<=r,则a的范围区间[nowA+(1<<j),nowA+(1<<(1+j))-1]必定符合,然后再考虑a的j位为0的情况,dfs下去。
其他情况同理。
标记范围可以用类似扫描线的方法,结合线段差分的思想。

int l[maxv],r[maxv],val[maxv];
bool vis[maxv];
void dfs(int p){
    vis[p]=1;
    for(int i=0;i<g[p].size();i++){
        int ver=g[p][i].first,edg=g[p][i].second;
        if(vis[ver]) continue;
        val[ver]=val[p]^edg;
        dfs(ver);
    }
}
vector<pair<int,int> > all[2],res;
void dfs1(int id,int now,int val,int nowR,int nowA){
    if(now==-1){
        all[id].push_back({nowA,nowA});
        return;
    }
    int x=((val>>now)&1),y=((nowR>>now)&1);
    if(y){
        if(x) all[id].push_back({nowA+(1<<now),nowA+(1<<(1+now))-1}),dfs1(id,now-1,val,nowR,nowA);
        else all[id].push_back({nowA,nowA+(1<<now)-1}),dfs1(id,now-1,val,nowR,nowA+(1<<now));
    }else{
        if(x) dfs1(id,now-1,val,nowR,nowA+(1<<now));
        else dfs1(id,now-1,val,nowR,nowA);
    }
}
int main(){
    int n,u,v,w,ans=0,sum=0;
    n=(int)read();
    for(int i=1;i<=n;i++) l[i]=(int)read(),r[i]=(int)read();
    for(int i=1;i<=n-1;i++) u=(int)read(),v=(int)read(),w=(int)read(),g[u].push_back(make_pair(v,w)),g[v].push_back(make_pair(u,w));
    dfs(1);  //求val[i]
    for(int i=1;i<=n;i++){  //求1<=val[i]^a<=l[i]-1与1<=val[i]^a<=r[i]
        if(l[i]>=1) dfs1(0,29,val[i],l[i]-1,0);
        dfs1(1,29,val[i],r[i],0);
    }
    for(int i=0;i<all[0].size();i++){
        int l=all[0][i].first,r=all[0][i].second;
        res.push_back({l,-1}),res.push_back({r+1,1});
    }
    for(int i=0;i<all[1].size();i++){
        int l=all[1][i].first,r=all[1][i].second;
        res.push_back({l,1}),res.push_back({r+1,-1});
    }
    sort(res.begin(),res.end());  //扫描线差分法求值为n的区间。
    for(int i=0;i<res.size()-1;i++){
        sum+=res[i].second;
        if(sum==n) ans+=res[i+1].first-res[i].first;
    }
    printf("%d",ans);
    return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
### 回答1: 可以使用Python编写一个程序来计算给定范围内所有数字的平方和。具体的代码如下: ```python start = int(input("请输入起始数字:")) end = int(input("请输入结束数字:")) sum = for i in range(start, end+1): sum += i**2 print("给定范围内所有数字的平方和为:", sum) ``` 这个程序首先要用户输入起始数字和结束数字,然后使用一个for循环来遍历这个范围内的所有数字,计算它们的平方并将结果累加到一个变量中。最后,程序输出这个变量的值,即给定范围内所有数字的平方和。 ### 回答2: Python可以用循环语句和数学运算实现给定范围内所有数字的平方和。 首先,我们需要输入一个数字范围,可以使用input()函数获取用户输入的值。我们可以使用int()函数将输入的字符串转换成整数类型。 接下来,我们需要使用for循环语句遍历指定范围内的所有数字,并计算每个数字的平方。然后将每个数字的平方值进行累加,得到最终的平方和。 具体实现如下: ``` # 获取用户输入的范围 start = int(input("请输入开始数字:")) end = int(input("请输入结束数字:")) # 计算平方和 sum = 0 for i in range(start, end + 1): sum += i ** 2 # 返回结果 print("范围内所有数字的平方和为:", sum) ``` 这里使用了range()函数来获取指定范围内的整数序列。在循环语句中,使用i来代表当前遍历到的数字,然后计算i的平方值并将其加到sum中。 最后,使用print()函数输出结果。结果将显示在控制台中,告诉用户输出的平方和是多少。 以上就是给定范围内所有数字的平方和的Python实现方法。使用这种方法可以自动计算平方和,避免手动计算出错的问题,提高了代码的效率和准确性。 ### 回答3: 要给定范围内所有数字的平方和,可以使用Python编程语言来完成。下面是实现方法: 1. 首先,需要定义给定范围,可以使用Python内置的range()函数来定义范围。代码如下: start = 1 # 范围的起始值 end = 10 # 范围的结束值 numbers = range(start, end+1) # 定义范围的列表 2. 接下来,需要遍历这个范围中的所有数字,并将每个数字的平方和。可以使用for循环来实现。代码如下: sum = 0 # 初始化总和为0 for num in numbers: sum += num**2 # 平方并累加到总和中 3. 最后,输出得到的总和即可。代码如下: print("范围", start, "到", end, "内所有数字的平方和为:", sum) 完整代码如下: start = 1 end = 10 numbers = range(start, end+1) sum = 0 for num in numbers: sum += num**2 print("范围", start, "到", end, "内所有数字的平方和为:", sum) 如果需要改变范围,只需要修改start和end的值即可。如果需要将范围扩展到更大的数值,可以使用Python的大整数算法。如果需要将程序优化,可以使用Python的并行计算、numpy等加速计算方法。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值