牛客竞赛_ACM/NOI/CSP/CCPC/ICPC算法编程高难度练习赛_牛客竞赛OJ
这题其实学的的东西挺多的
做法很多:这里介绍一种dfs的做法
暴力枚举1-n的点统计每一个点贡献的种类数
因为点数和边数都比较小所以直接开vector存了
我们可以发现二进制每次总的长度变长一位原来的数值就会扩大两倍:这个很好证明比如110
原来的贡献是
1*pow(2,2)+1*pow(2,1)+0*pow(2,0)
变长一位之后就是110?
那么原来的答案就是
1*pow(2,3)+1*pow(2,2)+0*pow(2,1)
很明显扩大两倍
所以每一次的答案就是:val*2+w[i]
w[i]是每一个节点是0还是1
void dfs(ll x,ll fa,ll val)
{
if(val>=l&&val<=r&&x!=fa)sum++;
for(auto to:g[x]){
if(to==fa)continue;
ll p=val*2+w[to];
if(p<=r)dfs(to,x,val*2+w[to]);
}
}
p<=r是可以做一个剪枝的如果大于r之后的点没必要搜了肯定是会大于r的
这里是x是当前节点,fa是x的祖先,val是现在的字串价值是多少
注意因为这里题目有说一个点是不可以当字符串的所以这里有一个技巧
dfs(i,i,w[i])
就是我下面循环的时候父节点本来之前的做法是当初-1的但是我们现在当成i自己这样
我们dfs第一次判断的时候有一个x!=fa就可以把单独一个点的情况排除掉了
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N=2e6+10;
vector<ll>g[N];
ll sum;
ll n,l,r;
ll dep;
string k;
ll w[N];
void dfs(ll x,ll fa,ll val)
{
if(val>=l&&val<=r&&x!=fa)sum++;
for(auto to:g[x]){
if(to==fa)continue;
ll p=val*2+w[to];
if(p<=r)dfs(to,x,val*2+w[to]);
}
}
int main()
{
cin>>n>>l>>r;
string s;
cin>>s;
for(int i=0;i<n;i++){
w[i+1]=s[i]-'0';
}
ll x,y;
for(int i=1;i<=n-1;i++){
cin>>x>>y;
g[x].push_back(y);
g[y].push_back(x);
}
for(int i=1;i<=n;i++)
{
dep=1;
dfs(i,i,w[i]);
}
cout<<sum<<endl;
}