Weak Pair

Time Limit: 4000/2000 MS (Java/Others)    Memory Limit: 262144/262144 K (Java/Others)

Total Submission(s): 1468    Accepted Submission(s): 472

Problem Description
You are given a rooted tree
of N nodes,
labeled from 1 to N.
To the ith
node a non-negative value ai is
assigned.An ordered pair
of nodes (u,v) is
said to be weak if

  (1) u is
an ancestor of v (Note:
In this problem a node u is
not considered an ancestor of itself);

  (2) au×av≤k.



Can you find the number of weak pairs in the tree?
 
Input
There are multiple cases in the data set.

  The first line of input contains an integer T denoting
number of test cases.

  For each case, the first line contains two space-separated integers, N and k,
respectively.

  The second line contains N space-separated
integers, denoting a1 to aN.

  Each of the subsequent lines contains two space-separated integers defining an edge connecting nodes u and v ,
where node u is
the parent of node v.



  Constrains: 

  

  1≤N≤105 

  

  0≤ai≤109 

  

  0≤k≤1018
 
Output
For each test case, print a single integer on a single line denoting the number of weak pairs in the tree.
 
Sample Input
1
2 3
1 2
1 2
 
Sample Output
1
这是一道很好的数据结构的题目:
可以用很多方法写
首先思路是:dfs这颗树,每到一个节点,都计算这个节点的祖先中满足条件的有几个
而计算这个就需要维护一个序列,并且高效的得出多少个祖先满足条件。
即在序列中找到小于k/a[i]的数有多少个,很容易想到用树状数组和线段树。
权值1e9需要离散化。
树状数组:
#include <iostream>
#include <string.h>
#include <stdlib.h>
#include <algorithm>
#include <math.h>
#include <stdio.h>
#include <map> using namespace std;
const int maxn=1e5;
typedef long long int LL;
int n;
LL k;
LL a[maxn+5];
struct Node
{
int value;
int next;
}edge[maxn*2+5];
int head[maxn+5];
int vis[maxn+5];
int tot;
int c[maxn*2+5];
LL b[maxn+5];
LL e[maxn*2+5];
map<LL,int> m;
void add(int x,int y)
{
edge[tot].value=y;
edge[tot].next=head[x];
head[x]=tot++;
}
int lowbit(int x)
{
return x&(-x);
}
void update(int x,int num)
{
while(x<=n*2)
{
c[x]+=num;
x+=lowbit(x);
}
}
int sum(int x)
{
int _sum=0;
while(x>0)
{
_sum+=c[x];
x-=lowbit(x);
}
return _sum;
}
LL ans;
void dfs(int root)
{
vis[root]=1;
for(int i=head[root];i!=-1;i=edge[i].next)
{
int v=edge[i].value;
if(!vis[v])
{
ans+=sum(m[b[v]]);
update(m[a[v]],1);
dfs(v);
update(m[a[v]],-1);
}
}
} void init()
{
memset(c,0,sizeof(c));
memset(vis,0,sizeof(vis));
memset(head,-1,sizeof(head));
tot=0;
}
int tag[maxn+5];
int main()
{
int t;
scanf("%d",&t);
int x,y;
while(t--)
{
scanf("%d%lld",&n,&k);
init();
int cnt=n;
m.clear();
for(int i=1;i<=n;i++)
{
scanf("%lld",&a[i]);
e[i]=a[i];
if(a[i]==0)
m[a[i]]=2*n;
else
{
b[i]=k/a[i];
e[++cnt]=b[i];
}
}
sort(e+1,e+cnt+1);
int tot=1;
for(int i=1;i<=cnt;i++)
{
if(!m.count(e[i]))
m[e[i]]=tot++;
}
memset(tag,0,sizeof(tag));
for(int i=1;i<=n-1;i++)
{
scanf("%d%d",&x,&y);
add(x,y);
tag[y]++;
}
int root;
for(int i=1;i<=n;i++)
{
if(tag[i]==0)
root=i;
}
ans=0;
update(m[a[root]],1);
dfs(root);
printf("%lld\n",ans);
}
return 0;
}

线段树:

<pre name="code" class="html">#include <iostream>
#include <string.h>
#include <algorithm>
#include <stdlib.h>
#include <math.h>
#include <stdio.h>
#include <string>
#include <map>
#include <vector> using namespace std;
typedef long long int LL;
const int maxn=1e5;
vector<int> v[maxn+5];
int sum[maxn*8+5];
int n;
LL k;
LL a[maxn+5];
LL b[maxn+5];
LL e[maxn*2+5];
int aa[maxn+5];
int bb[maxn+6];
map<LL,int> m; void PushUp(int node)
{
sum[node]=sum[node<<1]+sum[node<<1|1];
}
void update(int node,int begin,int end,int ind,int num)
{
if(begin==end)
{
sum[node]+=num*(end-begin+1);
return;
}
int m=(begin+end)>>1;
if(ind<=m)
update(node<<1,begin,m,ind,num);
else
update(node<<1|1,m+1,end,ind,num);
PushUp(node);
}
LL Query(int node,int begin,int end,int left,int right)
{
if(left<=begin&&end<=right)
return sum[node];
int m=(begin+end)>>1;
LL ret=0;
if(left<=m)
ret+=Query(node<<1,begin,m,left,right);
if(right>m)
ret+=Query(node<<1|1,m+1,end,left,right);
PushUp(node);
return ret;
}
int tag[maxn+5];
LL ans;
void dfs(int root)
{
int len=v[root].size();
for(int i=0;i<len;i++)
{
int w=v[root][i];
ans+=Query(1,1,2*n,1,bb[w]);
update(1,1,2*n,aa[w],1);
dfs(v[root][i]);
update(1,1,2*n,aa[w],-1);
}
}
void init()
{ memset(sum,0,sizeof(sum));
memset(tag,0,sizeof(tag));
}
int main()
{
int t;
scanf("%d",&t);
int x,y;
while(t--)
{
scanf("%d%lld",&n,&k);
int cnt=0;
init();
m.clear();
for(int i=1;i<=n;i++)
{
scanf("%lld",&a[i]);
e[++cnt]=a[i];
b[i]=k/a[i];
e[++cnt]=b[i];
v[i].clear();
}
sort(e+1,e+cnt+1);
int cot=1;
for(int i=1;i<=cnt;i++)
{
if(!m.count(e[i]))
m[e[i]]=cot++;
}
for(int i=1;i<=n;i++)
{
aa[i]=m[a[i]];
bb[i]=m[b[i]];
}
for(int i=1;i<=n-1;i++)
{
scanf("%d%d",&x,&y);
v[x].push_back(y);
tag[y]++;
}
int root;
for(int i=1;i<=n;i++)
{
if(tag[i]==0)
root=i;
}
ans=0;
update(1,1,2*n,m[a[root]],1);
dfs(root);
printf("%lld\n",ans);
}
return 0;
}

其实用线段树也可以不离散的方法做,是线段树的动态开点,动态开点就是用到了这个点再去开,不用的点不用开

这样在0到1e18的范围内,最多储存的点也就n个叶子节点,开个8*n的空间就足够了

</pre><pre code_snippet_id="1877993" snippet_file_name="blog_20160912_2_1715825" name="code" class="html"><pre name="code" class="html">#include <iostream>
#include <string.h>
#include <stdlib.h>
#include <algorithm>
#include <math.h>
#include <string>
#include <stdio.h>
#include <vector> using namespace std;
const int maxn=1e5;
const long long int len=1e18;
typedef long long int LL;
LL a[maxn+5];
LL b[maxn+5];
int n;
LL k;
vector<int> v[maxn+5];
struct Node
{
int lch,rch;
LL sum;
Node(){};
Node(int lch,int rch,LL sum)
{
this->lch=lch;
this->rch=rch;
this->sum=sum;
}
}tr[maxn*100+5];
int p;
void PushUp(int node)
{
tr[node].sum=tr[tr[node].lch].sum+tr[tr[node].rch].sum;
} int newnode()
{
tr[++p]=Node(-1,-1,0);
return p;
}
void update(int node,LL begin,LL end,LL ind,int num)
{
if(begin==end)
{
tr[node].sum+=num;
return;
}
LL m=(begin+end)>>1;
if(tr[node].lch==-1) tr[node].lch=newnode();
if(tr[node].rch==-1) tr[node].rch=newnode();
if(ind<=m)
update(tr[node].lch,begin,m,ind,num);
else
update(tr[node].rch,m+1,end,ind,num);
PushUp(node);
}
LL query(int node,LL begin,LL end,LL left,LL right)
{
if(node==-1)
return 0;
if(left<=begin&&end<=right)
return tr[node].sum;
LL m=(begin+end)>>1;
LL ret=0;
if(left<=m)
ret+=query(tr[node].lch,begin,m,left,right);
if(right>m)
ret+=query(tr[node].rch,m+1,end,left,right);
PushUp(node);
return ret; }
int tag[maxn+5];
LL ans;
void dfs(int root)
{
int len1=v[root].size();
for(int i=0;i<len1;i++)
{
int w=v[root][i];
ans+=query(1,0,len,0,b[w]);
update(1,0,len,a[w],1);
dfs(w);
update(1,0,len,a[w],-1);
}
}
void init()
{
memset(tag,0,sizeof(tag));
p=0;
newnode();
}
int main()
{
int t;
scanf("%d",&t);
int x,y;
while(t--)
{
scanf("%d%lld",&n,&k);
for(int i=1;i<=n;i++)
{
scanf("%lld",&a[i]);
b[i]=k/a[i];
v[i].clear();
}
init();
for(int i=1;i<=n-1;i++)
{
scanf("%d%d",&x,&y);
v[x].push_back(y);
tag[y]++;
}
int root;
for(int i=1;i<=n;i++)
{
if(!tag[i])
root=i;
}
ans=0;
update(1,0,len,a[root],1);
dfs(root);
printf("%lld\n",ans);
}
return 0;
}

还可以自下而上,用线段树的启发合并,计算每一个节点的所有子节点对他的贡献

关于线段树的启发式合并,有必要再写一篇博客总结一下

#include <iostream>
#include <string.h>
#include <stdlib.h>
#include <stdio.h>
#include <algorithm>
#include <math.h> using namespace std;
const int maxn=1e5;
typedef long long int LL;
int rt[maxn*100+5];
int ls[maxn*100+5];
int rs[maxn*100+5];
LL sum[maxn*100+5];
int a[maxn+5];
LL k;
int n;
int p;
int l,r;
int newnode()
{
sum[p]=ls[p]=rs[p]=0;
return p++;
}
void build(int &node,int begin,int end,LL val)
{
if(!node) node=newnode();
sum[node]=1;
if(begin==end) return;
LL mid=(begin+end)>>1;
if(val<=mid) build(ls[node],begin,mid,val);
else build(rs[node],mid+1,end,val);
}
LL Query(int node,int begin,int end,LL val)
{
if(!node||val<begin) return 0;
if(begin==end) return sum[node];
LL mid=(begin+end)>>1;
if(val<=mid) return Query(ls[node],begin,mid,val);
else return sum[ls[node]]+Query(rs[node],mid+1,end,val);
}
void mergge(int &x,int y, int begin,int end)
{
if(!x||!y) {x=x^y;return;}
sum[x]+=sum[y];
if(begin==end) return;
LL mid=(begin+end)>>1;
mergge(ls[x],ls[y],begin,mid);
mergge(rs[x],rs[y],mid+1,end);
}
struct Node
{
int value;
int next;
}edge[maxn*2+5];
int head[maxn+5];
int tot;
void add(int x,int y)
{
edge[tot].value=y;
edge[tot].next=head[x];
head[x]=tot++;
}
LL ans;
void dfs(int root)
{
for(int i=head[root];i!=-1;i=edge[i].next)
{
int w=edge[i].value;
dfs(w);
mergge(rt[root],rt[w],l,r);
}
ans+=Query(rt[root],l,r,k/a[root]);
if(k>=1ll*a[root]*a[root])
ans--;
}
int tag[maxn+5];
int main()
{
int t;
scanf("%d",&t);
int x,y;
while(t--)
{
scanf("%d%lld",&n,&k);
p=1;
memset(tag,0,sizeof(tag));
memset(head,-1,sizeof(head)); tot=0;
l=1e9;r=0;
for(int i=1;i<=n;i++)
{
scanf("%d",&a[i]);
l=min(l,a[i]);r=max(r,a[i]);
}
for(int i=1;i<=n;i++)
build(rt[i]=0,l,r,a[i]); for(int i=1;i<=n-1;i++)
{
scanf("%d%d",&x,&y);
add(x,y);
tag[y]++;
}
int root;
for(int i=1;i<=n;i++)
if(tag[i]==0) root=i;
ans=0;
dfs(root);
printf("%lld\n",ans);
}
return 0;
}

也可以用拓扑排序,自下而上进行启发式合并,

#include <iostream>
#include <string.h>
#include <stdlib.h>
#include <stdio.h>
#include <algorithm>
#include <math.h>
#include <queue> using namespace std;
const int maxn=1e5;
typedef long long int LL;
int rt[maxn*100+5];
int ls[maxn*100+5];
int rs[maxn*100+5];
LL sum[maxn*100+5];
int a[maxn+5];
int f[maxn+5];
LL k;
int n;
int p;
int l,r;
queue<int> q;
int newnode()
{
sum[p]=ls[p]=rs[p]=0;
return p++;
}
void build(int &node,int begin,int end,LL val)
{
if(!node) node=newnode();
sum[node]=1;
if(begin==end) return;
LL mid=(begin+end)>>1;
if(val<=mid) build(ls[node],begin,mid,val);
else build(rs[node],mid+1,end,val);
}
LL Query(int node,int begin,int end,LL val)
{
if(!node||val<begin) return 0;
if(begin==end) return sum[node];
LL mid=(begin+end)>>1;
if(val<=mid) return Query(ls[node],begin,mid,val);
else return sum[ls[node]]+Query(rs[node],mid+1,end,val);
}
void mergge(int &x,int y, int begin,int end)
{
if(!x||!y) {x=x^y;return;}
sum[x]+=sum[y];
if(begin==end) return;
LL mid=(begin+end)>>1;
mergge(ls[x],ls[y],begin,mid);
mergge(rs[x],rs[y],mid+1,end);
}
LL ans;
int tag[maxn+5];
int main()
{
int t;
scanf("%d",&t);
int x,y;
while(t--)
{
scanf("%d%lld",&n,&k);
p=1;
memset(tag,0,sizeof(tag));
l=1e9;r=0;
for(int i=1;i<=n;i++)
{
scanf("%d",&a[i]);
l=min(l,a[i]);r=max(r,a[i]);
}
for(int i=1;i<=n;i++)
build(rt[i]=0,l,r,a[i]); for(int i=1;i<=n-1;i++)
{
scanf("%d%d",&x,&y);
tag[x]++;
f[y]=x;
}
for(int i=1;i<=n;i++)
{
if(tag[i]==0)
q.push(i);
}
ans=0;
while(!q.empty())
{
int x=q.front();q.pop();
if(1LL*a[x]*a[x]<=k) ans--;
ans+=Query(rt[x],l,r,k/a[x]);
mergge(rt[f[x]],rt[x],l,r);
if(!--tag[f[x]]) q.push(f[x]); }
printf("%lld\n",ans);
}
return 0;
}

最后写一种,可持续化线段树的解法。首先将树形转成线形,然后逐个点插入,求一个根节点的子树对根节点的贡献,就是求DFS序列一段区间

小于k/a[i]的有多少个,可持续化线段树利用类似前缀和的原理,tree[r]-tree[l-1]就是l到r这一段区间所有点的线段树

#include <iostream>
#include <string.h>
#include <stdlib.h>
#include <stdio.h>
#include <algorithm>
#include <math.h>
#include <stack> using namespace std;
const int maxn=1e5;
typedef long long int LL;
int rt[maxn*100+5];
int ls[maxn*100+5];
int rs[maxn*100+5];
LL sum[maxn*100+5];
int p;
int n;
LL k;
int l,r;
void update(int &node,int l,int r,int val)
{ ls[p]=ls[node];rs[p]=rs[node];
sum[p]=sum[node];node=p;
p++; if(l==r)
{
sum[node]++;
return;
}
sum[node]++;
int mid=(l+r)>>1;
if(val<=mid) update(ls[node],l,mid,val);
else update(rs[node],mid+1,r,val);
}
LL query(int node,int l,int r,LL val)
{
if(val<l) return 0;
if(!node) return 0;
if(l==r) return sum[node];
LL mid=(l+r)>>1;
if(val<=mid) return query(ls[node],l,mid,val);
else return sum[ls[node]]+query(rs[node],mid+1,r,val);
}
struct Node
{
int value;
int next;
}edge[maxn*2+5];
int head[maxn+5];
int tot;
void add(int x,int y)
{
edge[tot].value=y;
edge[tot].next=head[x];
head[x]=tot++;
}
int res[maxn*2];
int a[maxn+5];
int cot;
void dfs(int root)
{
res[cot++]=root;
for(int i=head[root];i!=-1;i=edge[i].next)
{
int w=edge[i].value;
dfs(w);
}
res[cot++]=root;
}
int tag[maxn+5];
int flag[maxn+5];
int main()
{
int t;
scanf("%d",&t);
int x,y;
while(t--)
{
scanf("%d%lld",&n,&k);
l=1e9;r=0;
for(int i=1;i<=n;i++)
{
scanf("%d",&a[i]);
l=min(l,a[i]);r=max(r,a[i]);
} memset(head,-1,sizeof(head));
memset(tag,0,sizeof(tag));
tot=0;
p=1;
for(int i=1;i<=n-1;i++)
{
scanf("%d%d",&x,&y);
add(x,y);
tag[y]++;
}
int root;
for(int i=1;i<=n;i++)
{
if(!tag[i])
root=i;
}
cot=0;
dfs(root);
memset(flag,0,sizeof(flag));
update(rt[res[0]],l,r,a[res[0]]);
flag[res[0]]=1;
LL ans=0;
int now=0;
for(int i=1;i<cot;i++)
{
if(flag[res[i]]==1)
{
LL ans1=query(rt[res[now]],l,r,k/a[res[i]]);
LL ans2=query(rt[res[i]],l,r,k/a[res[i]]);
//cout<<ans1<<" "<<ans2<<endl;
ans+=ans1-ans2;
continue;
}
flag[res[i]]=1;
update(rt[res[i]]=rt[res[now]],l,r,a[res[i]]);
now=i;
}
printf("%lld\n",ans);
}
return 0;
}


HDU 5877 2016大连网络赛 Weak Pair(树状数组,线段树,动态开点,启发式合并,可持久化线段树)的更多相关文章

  1. 2016大连网络赛 Weak Pair

    Weak Pair Time Limit: 4000/2000 MS (Java/Others)    Memory Limit: 262144/262144 K (Java/Others) Prob ...

  2. 2016 大连网赛---Weak Pair(dfs+树状数组)

    题目链接 http://acm.split.hdu.edu.cn/showproblem.php?pid=5877 Problem Description You are given a rooted ...

  3. 2016大连网络赛 Different GCD Subarray Query

    Different GCD Subarray Query Time Limit: 6000/3000 MS (Java/Others)    Memory Limit: 65536/65536 K ( ...

  4. HDU 5869 Different GCD Subarray Query(2016大连网络赛 B 树状数组+技巧)

    还是想不到,真的觉得难,思路太巧妙 题意:给你一串数和一些区间,对于每个区间求出区间内每段连续值的不同gcd个数(该区间任一点可做起点,此点及之后的点都可做终点) 首先我们可以知道每次添加一个值时gc ...

  5. 2016大连网络赛 Football Games

    Football Games Time Limit: 2000/1000 MS (Java/Others)    Memory Limit: 65536/65536 K (Java/Others) P ...

  6. 2016大连网络赛 Function

    Function Time Limit: 7000/3500 MS (Java/Others)    Memory Limit: 262144/262144 K (Java/Others) Probl ...

  7. 2016大连网络赛 Sparse Graph

    Sparse Graph Time Limit: 4000/2000 MS (Java/Others)    Memory Limit: 262144/262144 K (Java/Others) P ...

  8. HDU - 5878 2016青岛网络赛 I Count Two Three(打表+二分)

    I Count Two Three 31.1% 1000ms 32768K   I will show you the most popular board game in the Shanghai ...

  9. HDU - 5887 2016青岛网络赛 Herbs Gathering(形似01背包的搜索)

    Herbs Gathering 10.76% 1000ms 32768K   Collecting one's own plants for use as herbal medicines is pe ...

随机推荐

  1. 获取http内容的php函数

    实现获取http内容的php函数. 代码如下: <?php function http_open($url, $data, $cookie = null, $method = "GET ...

  2. utubu远程

    http://www.linuxidc.com/Linux/2014-04/100491.htm 首先安装xfce: sudo apt-get update sudo apt-get install ...

  3. Java多线程简析——Synchronized(同步锁)、Lock以及线程池

    Java多线程 Java中,可运行的程序都是有一个或多个进程组成.进程则是由多个线程组成的.最简单的一个进程,会包括mian线程以及GC线程. 线程的状态 线程状态由以下一张网上图片来说明: 在图中, ...

  4. How to Install Xcode, Homebrew, Git, RVM, Ruby & Rails on Snow Leopard, Lion, Mountain Lion, and Mavericks

    After following many outdated and incomplete instructions for setting up a web development environme ...

  5. nodejs之util工具

    util是nodejs的一大核心模块,用来提供常用函数的集合 1.util.inherits(实现对象原型继承) 概要:js的继承是基于原型的,本身并没有继承的语言特性,仅仅是通过复制原型的方式来实现 ...

  6. 2018.7.13vue知识小结

    //配置是否允许vue-devtools检查代码,方便调试,生产环境中需要设置为false Vue.config.devtools=false; Vue.config.productionTip=fa ...

  7. vue中config/index.js:配置的详细理解

    当我们需要和后台分离部署的时候,必须配置config/index.js: 用vue-cli 自动构建的目录里面  (环境变量及其基本变量的配置) var path = require('path') ...

  8. 使用JSTL的sql:query标签制作分页查询遇到NoSuchFieldError: deferredExpression

    参考:http://hi.baidu.com/desyle/item/4fe650265792d7182a0f1c33 症状: 如题所述,代码如下 <sql:query var="re ...

  9. 如何在iOS上实现对HTTPS的支持(转)

    原文地址:http://blog.5ibc.net/p/101504.html 首先,需要明确你使用HTTP/HTTPS的用途,因为OSX和iOS平台提供了多种API,来支持不同的用途,官方文档< ...

  10. SpringBoot DataSource 配置说明

    DataSource 配置说明 属性 说明 spring.dao.exceptiontranslation.enabled 是否开启PersistenceExceptionTranslationPos ...