BZOJ3365: [Usaco2004 Feb]Distance Statistics 路程统计 点分治

题目链接:http://www.lydsy.com/JudgeOnline/problem.php?id=3365


题解:点分治的题

(对于点分治初学者,简单概括一下点分治):点分治通过递归的方式不断找到子树的重心,然后以当前的子树的重心为根,统计该子树内所有点到根(重心)的距离并存放到一个数组里,然后在距离数组里做一些事情。点分治的时间复杂度为nlogn*(对距离数组做的事情的时间复杂度)

树的重心:找到一个点,使其作为树根,其最大子树的节点个数最少

说这道题:点分治裸题,把距离数组排序后用双指针l,rO(n)扫描,对于每一个l,有多少个l<i<=r使得len[i]+len[l]<=k,最后输出答案(因为点分治这种做法会有在同一颗子树的两个距离被计算入答案的情况,所以要把其所有子树的答案删除

#include<iostream>
#include<cstdio>
#include<cmath>
#include<cstring>
#include<algorithm>
using namespace std;
const int inf=1e9+7;
const int N=80005;
const int M=160005;
int maxn,siz[N],s[N],dis[N];
int n,m,K,cnt,G,sum,ans,h,lj[N],nxt[M],w[M],to[M];
bool vis[N];
void ins(int f,int t,int p) {cnt++,to[cnt]=t,nxt[cnt]=lj[f],lj[f]=cnt,w[cnt]=p;}
void add(int f,int t,int p) {ins(f,t,p),ins(t,f,p);}
void findG(int x,int fa)
{
    siz[x]=1;
	int mx=0;
    for(int i=lj[x];i;i=nxt[i])
    if(to[i]!=fa&&!vis[to[i]])
    {
        findG(to[i],x);
        siz[x]+=siz[to[i]];
        mx=max(mx,siz[to[i]]);
    }
    mx=max(mx,sum-siz[x]);
    if(mx<maxn) maxn=mx,G=x;
}
void dfs(int x,int fa)
{
    s[++h]=dis[x];
    for(int i=lj[x];i;i=nxt[i])
    if(to[i]!=fa&&!vis[to[i]])
    {
        dis[to[i]]=dis[x]+w[i];
        dfs(to[i],x);
    }
}
int cal(int x,int now)
{
	dis[x]=now;
	h=0;
    dfs(x,0);
    sort(s+1,s+h+1);
    int ans=0,l=1,r=h;
    while(l<r)
    {
		if(s[l]+s[r]<=K) ans+=r-l,l++;
		else r--;
    }
    return ans;
}
void solve(int x)
{
    ans+=cal(x,0);
    vis[x]=1;
    for(int i=lj[x];i;i=nxt[i])
    if(!vis[to[i]])
    {
        ans-=cal(to[i],w[i]);
        G=0;
        maxn=inf;
		sum=siz[to[i]];
        findG(to[i],0);
        solve(G);
    }
}
int main()
{
	scanf("%d%d",&n,&m);
	int u,v,w;
    char ss[5];
	for(int i=1;i<=m;i++)
    {
    	scanf("%d%d%d%s",&u,&v,&w,&ss);
    	add(u,v,w);
    }
    scanf("%d",&K);
    G=0;
	sum=n,maxn=inf;
    findG(1,0);
    solve(G);
    printf("%d\n",ans);
}



  • 3
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值