hdu5877
题目
就是给你一颗有根树,对于两个有序节点对u,v满足两个条件,u是v的父亲,au*av<=k则算作是一个Weak Pair,求总Weak Pair。
思路
一开始还想往树型dp套套套,结果队友提醒了下,但是没注意到有根树,离散化的也不是很好,没A。
其实无非就是区间查询,单点增删的线段树,离散化要把k/a[i]也给离散化了。对于一个节点先查询来到它的路径上满足要求的个数,然后插入它,然后递归子节点,在结束时删除它即可。
代码
#include <cstdlib>
#include <cstring>
#include <algorithm>
#include <vector>
#include <cmath>
#define lson l,m,rt<<1
#define rson m+1,r,rt<<1|1
using namespace std;
typedef long long ll;
const ll maxn=1e5+100;
int n,numm,head[maxn],tot,deep[maxn];
ll num[maxn],ans,k,x[2*maxn];
ll sum[(2*maxn)<<2];
void PushUP(ll rt)
{
sum[rt]=sum[rt<<1]+sum[rt<<1|1];
}
void build(ll l,ll r,ll rt)
{
if(l==r)
{
sum[rt]=0;
return;
}
ll m=(l+r) >> 1;
build(lson);
build(rson);
PushUP(rt);
}
void update(ll p,ll add,ll l,ll r,ll rt)
{
if(l==r)
{
sum[rt]+=add;
return;
}
ll m=(l+r) >> 1;
if(p<=m) update(p,add,lson);
else update(p,add,rson);
PushUP(rt);
}
ll query(ll L,ll R,ll l,ll r,ll rt)
{
if(L<=l&&r<=R)
{
return sum[rt];
}
ll ret=0;
ll m=(l+r) >> 1;
if(L<=m) ret+=query(L,R,lson);
if(R>m) ret+=query(L,R,rson);
return ret;
}
struct node
{
int next;
int to;
} edge[maxn];
void addedge(int from,int to)
{
edge[tot].to=to;
edge[tot].next=head[from];
head[from]=tot++;
}
void dfs(int u,int fa)
{
int o=lower_bound(x,x+numm,k/num[u])-x;
ans+=query(0,o,0,numm,1);
int kk=lower_bound(x,x+numm,num[u])-x;
update(kk,1,0,numm,1);
for(int i=head[u]; ~i; i=edge[i].next)
{
int v=edge[i].to;
dfs(v,u);
}
update(kk,-1,0,numm,1);
}
int main()
{
int T;
scanf("%d",&T);
while(T--)
{
scanf("%d %I64d",&n,&k);
int cnt=0;
for(int i=1; i<=n; i++)
{
scanf("%I64d",&num[i]);
x[cnt++]=num[i];
}
for(int i=1; i<=n; i++)
x[cnt++]=k/num[i];
sort(x,x+cnt);
numm=unique(x,x+cnt)-x;
tot=0;
memset(head,-1,sizeof(head));
memset(deep,0,sizeof(deep));
for(int i=0; i<n-1; i++)
{
int u,v;
scanf("%d %d",&u,&v);
addedge(u,v);
deep[v]++;
}
ans=0;
build(0,numm,1);
for(int i=1; i<=n; i++)
if(deep[i]==0)
dfs(i,-1);
printf("%I64d\n",ans);
}
return 0;
}