poj 1741 Tree(给定一棵树,对于两个不同的节点a,b,满足dist(a,b,)<=k的点对数)
来源:互联网 发布:2009的淘宝店 编辑:程序博客网 时间:2024/06/03 08:48
参考:树分治论文
传送门:poj 1741 Tree
题意:给你一棵N(N<=10000)个节点的带权数,定义dist(u,v)为u,v两点之间的距离,再给定一个K
(1<=K<=10^9),如果对于两个不同的节点a,b,如果满足dist(a,b,)<=k,则称(a,b)为合法点对,
求合法点对数目
思路:我们知道一条路径要么过根节点,要么在一棵子树中,所以我们可以利用分治
路径在子树中的情况只需递归处理即可, 下面我们来分析如何处理路径过根结点的情况。
记depth[i]为点i到根节点的路径长度,Belong[i]=x(x为根节点的某个儿子,且节点i在以X为根的子树中)
那么我们要统计的就是:满足depth[i]+depth[j]<=k且Belong[i]!=Belong[j]的(i,j)个数=
depth[i]+depth[j]<=k的个数-满足depth[i]+depth[j]<=k且Belong[i]=Belong[j]的(i,j)个数
而对于这两个部分, 都是要求出满足Ai+Aj<=k的(i,j)的对数
将 A 排序后利用单调性我们很容易得出一个O(n)的算法,所以我们
可以用O(NlogN)的时间来解决这个问题。
所以总的时间复杂度为O(NlogN^2)
#include <map>#include <set>#include <stack>#include <queue>#include <cmath>#include <ctime>#include <vector>#include <cstdio>#include <cctype>#include <cstring>#include <cstdlib>#include <iostream>#include <algorithm>using namespace std;#define INF 0x3f3f3f3f#define inf -0x3f3f3f3f#define lson l,m,rt<<1#define rson m+1,r,rt<<1|1#define mem0(a) memset(a,0,sizeof(a))#define mem1(a) memset(a,-1,sizeof(a))#define mem(a, b) memset(a, b, sizeof(a))#define MP(x,y) make_pair(x,y)typedef long long ll;void fre() { freopen("input.in", "r", stdin); freopen("output.out", "w", stdout); }template <class T1, class T2>inline void gmax(T1 &a, T2 b) { if (b>a)a = b; }template <class T1, class T2>inline void gmin(T1 &a, T2 b) { if (b<a)a = b; }typedef pair<int,int>PI;const int maxn=10010;vector<PI>G[maxn];vector<int>dep;int dis[maxn],k,size[maxn],f[maxn],Count,root;//Count表示当前子树的结点的总个数bool Del[maxn];long long ans=0;void getroot(int u,int pre){ size[u]=1,f[u]=0; for(int i=0;i<G[u].size();++i){ int v=G[u][i].first; if(v!=pre && !Del[v]){ getroot(v,u); size[u]+=size[v]; f[u]=max(f[u],size[v]); } } f[u]=max(f[u],Count-size[u]); if(f[u]<f[root]) root=u;}void getdep(int u,int pre){ //这里还需要重新计算每个子树的size dep.push_back(dis[u]); size[u]=1; for(int i=0;i<G[u].size();++i){ int v=G[u][i].first; if(v!=pre && !Del[v]){ dis[v]=dis[u]+G[u][i].second; getdep(v,u); size[u]+=size[v]; } }}long long cal(int u,int bg_depth){ dep.clear();dis[u]=bg_depth; getdep(u,0); sort(dep.begin(),dep.end()); long long ret=0; for(int l=0,r=dep.size()-1;l<r;){ if(dep[l]+dep[r]<=k) ret+=(r-l),l++; else r--; } return ret;}void work(int u){ ans+=cal(u,0); Del[u]=true; for(int i=0;i<G[u].size();i++){ int v=G[u][i].first; if(!Del[v]){ ans-=cal(v,G[u][i].second); f[0]=Count=size[v]; getroot(v,root=0); work(root); } }}int main(){ int n; while(scanf("%d%d",&n,&k)!=EOF){ if(n==0&&k==0) break; for(int i=1;i<=n;i++) G[i].clear(); int u,v,w; for(int i=1;i<n;i++){ scanf("%d%d%d",&u,&v,&w); G[u].push_back(MP(v,w)); G[v].push_back(MP(u,w)); } ans=0; f[0]=Count=n; getroot(1,root=0); memset(Del,false,sizeof(Del)); work(root); printf("%lld\n",ans); } return 0;}
思路二:首先,我们先对每个点算出它的子节点的子树的最大是哪一个结点
然后每次合并都是把其他结点数较小的合并到大的上面,可以发现每个点最多被合并logn次
要先计算贡献再进行合并
/*思路:*/#include <map>#include <set>#include <stack>#include <queue>#include <cmath>#include <ctime>#include <vector>#include <cstdio>#include <cctype>#include <cstring>#include <cstdlib>#include <iostream>#include <algorithm>using namespace std;#define INF 0x3f3f3f3f#define inf -0x3f3f3f3f#define lson l,m,rt<<1#define rson m+1,r,rt<<1|1#define mem0(a) memset(a,0,Countof(a))#define mem1(a) memset(a,-1,Countof(a))#define mem(a, b) memset(a, b, Countof(a))#define MP(x,y) make_pair(x,y)typedef long long ll;typedef pair<int,int>PI;const int maxn=10010;vector<PI>G[maxn];long long ans;int k,Count[maxn],length[maxn],son[maxn];int size[maxn*30],root[maxn],tot,cnt[maxn*30],val[maxn*30],ch[maxn*30][2],fa[maxn*30];//按值的大小建立Splayvoid Newnode(int &now,int father,int k,int siz){ now=++tot; cnt[now]=siz,size[now]=siz,val[now]=k,fa[now]=father,ch[now][0]=ch[now][1]=0;}void dfs1(int u,int pre){ Count[u]=1,son[u]=-1; for(int i=0;i<G[u].size();++i){ int v=G[u][i].first; if(v!=pre){ dfs1(v,u); Count[u]+=Count[v]; if(son[u]==-1||Count[son[u]]<Count[v]) son[u]=v,length[u]=G[u][i].second; } }}void pushup(int x){ size[x]=size[ch[x][0]]+size[ch[x][1]]+cnt[x];}//旋转,kind为1为右旋,kind为0为左旋void Rotate(int x,int kind){ int y=fa[x]; ch[y][!kind]=ch[x][kind]; fa[ch[x][kind]]=y; //如果父节点不是根结点,则要和父节点的父节点连接起来 if(fa[y]) ch[fa[y]][ch[fa[y]][1]==y]=x; fa[x]=fa[y]; ch[x][kind]=y; fa[y]=x; pushup(y);}//Splay调整,将根为now的子树调整为goalvoid Splay(int now,int goal,int Belong){ while(fa[now]!=goal){ if(fa[ fa[now] ]==goal) Rotate(now,ch[ fa[now] ][0]==now); else{ int pre=fa[now],kind=ch[ fa[pre] ][0]==pre; //左儿子为1,右儿子为0 if(ch[pre][kind]==now){ //两个方向不同 Rotate(now,!kind); Rotate(now,kind); } else{ //两个方向相同 Rotate(pre,kind); Rotate(now,kind); } } } if(goal==0) root[Belong]=now; pushup(now);}int getans(int u,int dep,int dis){ if(!u) return 0; int ret=0; if(val[u]+dis-2*dep<=k){ ret+=size[ch[u][0]]+cnt[u]; ret+=getans(ch[u][1],dep,dis); } else ret+=getans(ch[u][0],dep,dis); return ret;}int Insert(int now,int k,int Belong,int siz){ while(ch[now][val[now]<k]){ //不重复插入 if(val[now]==k){ Splay(now,0,Belong); cnt[now]+=siz; pushup(now); return 0; } now=ch[now][val[now]<k]; } if(val[now]==k){ Splay(now,0,Belong); cnt[now]+=siz; pushup(now); return 0; } Newnode(ch[now][k>val[now]],now,k,siz); //将新插入的结点更新至根结点 Splay(ch[now][k>val[now]],0,Belong); return 1;}void query(int now,int u,int dep){ if(!u) return ; if(val[u]!=-INF&&val[u]!=INF) ans+=1LL*(getans(now,dep,val[u])-1)*cnt[u]; query(now,ch[u][0],dep); query(now,ch[u][1],dep);}void merge(int u,int Belong){ if(!u) return ; if(val[u]!=-INF&&val[u]!=INF) Insert(root[Belong],val[u],Belong,cnt[u]); merge(ch[u][0],Belong); merge(ch[u][1],Belong);}void dfs2(int u,int dep,int pre){ if(son[u]!=-1){ dfs2(son[u],dep+length[u],u); root[u]=root[son[u]]; ans+=getans(root[u],dep,dep)-1; Insert(root[u],dep,u,1); } else{ root[u]=0; Newnode(root[u],0,-INF,1); Newnode(ch[root[u]][1],root[u],INF,1);//头尾各加入一个空位 Newnode(ch[ch[root[u]][1]][0],ch[root[u]][1],dep,1); pushup(ch[root[u]][1]); pushup(root[u]); } for(int i=0;i<G[u].size();++i){ int v=G[u][i].first; if(v!=pre && v!=son[u]){ dfs2(v,dep+G[u][i].second,u); query(root[u],root[v],dep); merge(root[v],u); } }}int main(){ int n; while(scanf("%d%d",&n,&k)!=EOF){ tot=0; if(n==0&&k==0) break; for(int i=1;i<=n;i++) G[i].clear(); int u,v,w; for(int i=1;i<n;i++){ scanf("%d%d%d",&u,&v,&w); G[u].push_back(MP(v,w)); G[v].push_back(MP(u,w)); } dfs1(1,0); ans=0; dfs2(1,0,0); printf("%lld\n",ans); } return 0;}
0 0
- poj 1741 Tree(给定一棵树,对于两个不同的节点a,b,满足dist(a,b,)<=k的点对数)
- BZOJ 2301 Problem B(x属于[a,b],y属于[c,d]满足gcd(x,y)=k的(x,y)的有序对数)
- 给定正整数b,求最大的整数a,满足a*(a+b) 为完全平方数
- 给定两个正整数A和B,把A变成B需要几位?也就是说A和B之间的位数有多少个是不同的?
- a+=b与a=a+b的不同
- 对于给定的整数集合S,求出最大的d,使得a+b+c=d。
- 在数组中找出两个数a、b,使得a加b等于给定的c
- 给定一数组,输出满足2a=b(a,b代表数组中的数)的数对,要求时间复杂度尽量低。
- 求 区间[a,b]内满足p^k*q*^m(k>m)的数的个数
- 1/n=1/a+1/b(a<b)的对数
- 找出唯一的满足a + b + c = 1000的毕达哥拉斯三元组{a, b, c}。
- 输出满足n=a!+b!+c!的所有三位数
- 输出满足2a=b的数对
- 两个排序数组中求第k大的sum(a+b)
- 【算法】多叉树寻找A\B节点的分支点
- 打印给定位数的a,b 组合
- 给定两个正整数(二进制形式表示)A和B,问把A变为B需要改变多少位(bit)?也就是说,整数A和B的二进制表示中有多少位是不同的?
- 给定两个正整数(二进制形式表示)A和B,问把A变为B需要改变多少位(bit)?也就是说,整数A和B的二进制表示中有多少位是不同的?
- iOS推送开关
- 【Git】Eclipse上Git忽略文件不起作用
- 贪心法基础
- TensorLayer : The Fastest Deep Learning Library for TensorFlow
- 日志管理
- poj 1741 Tree(给定一棵树,对于两个不同的节点a,b,满足dist(a,b,)<=k的点对数)
- 浅谈标准I/O缓冲区
- net嵌入页面的几种方法
- Oracle Instant Client(即时客户端) 安装与配置
- 木块问题(vector)-UVa 101
- 最长公共子序列(LCS---动态规划)
- Jquery通过append新元素之后事件绑定问题的解决方案
- MAP和XML之间的转换
- 桌面右键菜单,添加程序的方法