标签:复杂 fine 序列 count str log poj1741 line mem
好久没有更新博文了,这里更新一发~~
5 4 1 2 3 1 3 1 1 4 2 3 5 1 0 0Sample Output
8
简单翻译一下,给定你若干条边,然后让你求任意两点之间的距离小于等于k的有多少对,暴力n^2肯定要炸,于是我们就可以想到用树分治。
我们发现总共只有2种情况,经过根节点和不经过根节点。
设d[i]为i节点到根节点的距离(注意:根节点是会变动的,因为我们每一次都要求一次重心,为了不使树退化成一条链,于是根节点就是重心节点是变动的)
d[i]+d[j]<=k就是表示经过根节点,我们发现排序之后d[i]是有序的,于是我们用两个指针在序列中进行搜索就可以了这里复杂度为on
然后求剩下不经过根节点的情况的时候我们就在他自身的子节点中同样进行上述的操作就可以了,注意要求重心!!
然后综合一下,此题使用树的分治算法时间复杂度为O(nlog^2n) 。
接下来放上一份我抄过来的代码,明天应该还要再写一遍的~
这代码感觉思路比较的清晰,我做了一些改动。
#pragma GCC optimize("O2")
#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<vector>
#define maxn 20000
using namespace std;
int s[maxn],f[maxn],d[maxn],book[maxn],n,k,root,ans,size;
struct Node{int to,w;};
vector<Node> g[maxn];
vector<int> dep;
int getroot(int x,int fa)
{
int u;
s[x]=1,f[x]=0;
for(int i=0;i<g[x].size();i++)
{
u=g[x][i].to;
if(u!=fa&&!book[u])
{
getroot(u,x);
s[x]+=s[u];
f[x]=max(f[x],s[u]);
}
}
f[x]=max(f[x],size-s[x]);
if(f[x]<f[root]) root=x;
}
void getdep(int x,int fa)
{
dep.push_back(d[x]);s[x]=1;
for(int i=0;i<g[x].size();i++)
{
int u=g[x][i].to;
if(u==fa||book[u]) continue;
d[u]=d[x]+g[x][i].w;
getdep(u,x);
s[x]+=s[u];
}
}
int calc(int x,int init)
{
dep.clear(),d[x]=init;
getdep(x,0);
int ans=0;
sort(dep.begin(),dep.end());
for(int l=0,r=dep.size()-1;l<r;)//这里细节要注意
if(dep[l]+dep[r]<=k) ans+=r-l,l++;
else r--;
return ans;
}
void work(int x)
{
ans+=calc(x,0),book[x]=1;
for(int i=0;i<g[x].size();i++)
{
int u=g[x][i].to;
if(book[u]) continue;
ans-=calc(u,g[x][i].w);
f[0]=size=s[u];
getroot(u,root=0);
work(root);
}
}
int main()
{
while(cin>>n>>k&&n&&k)
{
memset(book,0,sizeof(book));
for(int i=1;i<=n;i++) g[i].clear();
for(int i=1;i<n;i++)
{
int u,v,w;
scanf("%d%d%d",&u,&v,&w);
g[u].push_back((Node){v,w});
g[v].push_back((Node){u,w});
}
f[0]=size=n;
getroot(1,root=0);
ans=0;
work(root);
printf("%d\n",ans);
}
return 0;
}
也就90行不算多~up++
标签:复杂 fine 序列 count str log poj1741 line mem
原文地址:http://www.cnblogs.com/foreverpiano/p/7113363.html