题目:http://acm.hdu.edu.cn/showproblem.php?pid=5877
题意:给定一棵树(带根),定义weak pair 为1.u为v的祖先,2.a(u)*a(v)<=k,求这棵树总共有多少个这样的weak pair。输入说明:第一行输入测试样例的个数,第二行是n和k,n为树的节点数,k为限定值,接下来一行输入n个数,表示a(i),接下来n-1行表示各个点的连接,其中前面的点位父节点,后面的点位子节点
题解:先用map对a进行离散化处理,因为总共10W的点,但数据表示范围很大,树状数组完全存不下,所以要离散化,然后找出根结点,从根结点遍历子节点,每次遍历的时候先统计之前加入的满足a(u)<=k/a(v)的点的个数,即存在多少个weak pair,再将当前点加入树状数组,进行子节点的遍历,遍历完所有子节点后要进行删除当前节点,以后的节点不会再用到当前的节点,最后得出答案
代码:
#include<iostream>
#include<cstdio>
#include<cstring>
#include<string>
#include<algorithm>
#include<map>
#include<vector>
#define INF 2e18
using namespace std;
long long value[200050];
int c[200050];
long long k;
int n;
long long sum;
long long v[200050];
map<long long,int> m;//用于离散化
vector<int> son[100050];//用于存储子节点
bool father[100050];//用于标记是否有父亲,主要为找到root
int lowbit(int x)
{
return x & (-x);
}
void add(int x,int d)
{
for(;x<=2*n+2;x+=lowbit(x))
{
c[x]+=d;
}
}
int sumc(int x)
{
int s=0;
for(;x>0;x-=lowbit(x))
{
s+=c[x];
}
return s;
}
void dfs(int root)//遍历
{
sum+=sumc(m[v[n+root]]);
add(m[v[root]],1);//加入当前点
for(int i=0;i<son[root].size();i++)
{
dfs(son[root][i]);
}
add(m[v[root]],-1);//去掉当前点
son[root].clear();
}
int main()
{
int t;
scanf("%d",&t);
while(t--)
{
scanf("%d %lld",&n,&k);
for(int i=1;i<=n;i++)
{
scanf("%lld",&v[i]);
if(v[i]) v[n+i]=k/v[i];
else v[n+i]=INF;
value[i]=v[i];value[n+i]=v[n+i];
}
sort(value+1,value+2*n+1);
int cnt=1;
for(int i=1;i<=2*n;i++)
{
if(!m[value[i]]) m[value[i]]=cnt++;
}
memset(father,0,sizeof(father));
for(int i=1;i<=n-1;i++)
{
int f,s;
scanf("%d %d",&f,&s);
son[f].push_back(s);
father[s]=1;
}
int root;
for(int i=1;i<=n;i++)//找root
{
if(!father[i]){root=i;break;}
}
memset(c,0,sizeof(c));
sum=0;
dfs(root);
printf("%lld\n",sum);
m.clear();
}
return 0;
}

本文介绍了一种使用离散化处理和树状数组解决特定类型组合计数问题的方法,通过实例讲解了如何在给定一棵带根树的情况下,计算满足特定条件的节点对数量。
452

被折叠的 条评论
为什么被折叠?



