poj 1741 Tree(给定一棵树,对于两个不同的节点a,b,满足dist(a,b,)<=k的点对数)
2016-08-28 11:09
148 查看
参考:树分治论文
传送门: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)
思路二:首先,我们先对每个点算出它的子节点的子树的最大是哪一个结点
然后每次合并都是把其他结点数较小的合并到大的上面,可以发现每个点最多被合并logn次
要先计算贡献再进行合并
传送门: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]; //按值的大小建立Splay void 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的子树调整为goal void 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; }
相关文章推荐
- 给定两个整数 l 和 r ,对于所有满足1 ≤ l ≤ x ≤ r ≤ 10^9 的 x ,把 x 的所有约数全部写下来。对于每个写下来的数,只保留最高位的那个数码。求1~9每个数码出现的次数。
- poj 2486 树形dp(给定步数 走一棵树,获得的节点值总和的最大值)
- hdu6035 Colorful Tree 树形dp 给定一棵树,每个节点有一个颜色值。定义每条路径的值为经过的节点的不同颜色数。求所有路径的值和。
- 写了个比较两个字符串在哪个位置不同的工具,对于经常比较两字符串微小不同的朋友有用处
- 节点文件将两个不同格式的XML文件,进行节点对照,并生成一个用于对照功能的XML
- poj 1986(tarjan求树中两个节点的距离)
- SQL SERVER 2000 的企业管理器与查询分析器对于同一SQL语句有两个不同查询结果!
- TOJ 2241. Ancestor 【判断一棵树上两个节点的关系】
- POJ, 1330 Nearest Common Ancestors(找出两个节点最近的公共节点——BFS)
- 将两个不同格式的XML文件,进行节点对照,并生成一个用于对照功能的XML
- 将两个不同格式的XML文件,进行节点对照,并生成一个用于对照功能的XML
- 给定两个正整数A和B,把A变成B需要几位?也就是说A和B之间的位数有多少个是不同的?
- 假设有一个rand(0,1)的0,1随机生成器,如何对于给定的(a,b),随机生成一个x, 其中 a <= x <= b
- @陈利人 : #面试题#给定两个数组X和Y,元素都是正数。请找出满足如下条件的数对的数目: x^y > y^x,即x的y次方>y的x次方;x来自X数组,y来自Y数组
- <script>中text/javascript和application/javascript两个不同属性的区别
- JAVA获取两个List<String>中不同的数据
- (续)寻找一棵树上最远的两个节点
- 寻找一棵树上最远的两个节点
- 有两个序列A和B,A=(a1,a2,...,ak),B=(b1,b2,...,bk),A和B都按升序排列,对于1<=i,j<=k,求k个最小的(ai+bj),要求算法尽量高效
- 给定两个正整数A和B,把A变成B需要几位?也就是说A和B之间的位数有多少个是不同的?