bzoj1468: Tree 点分治

今天考试,依旧打暴力咯。一看第一题绝壁是个虚树之类的东东。就是给你一棵树每次选几个点,给定几个点的控制范围,统计有多少点被控制。正解是离线+点分治+虚树。搞得我整个人都BB了。不过这场真是暴力出奇迹,竟然混到rank9 我也是醉了,网络赛就是水。。。。。

说正题:学习了一下点分治,我们从最基本的问题入手。LCT男人八题。不知不觉搞完一半了。。。。。

点分治的思想和分治差不多就是把一个东西分块,分成若干子问题,再合并。

这里为了保证分治的最优性我们要找树的重心,这样就能保证是2nlogn的复杂度

对于每个点我们先统计所有经过它的点对,再减去每个在同一个子树上的点对。

注意为了防止统计子树的时候重复统计其包含父节点的情况 我们要把每个统计完的点标记一下。

然后同一个子树里的点应该加上 这段边长*2 由于我的代码里面每个点的距离都+=次边边长,所以calc里面只用传原边长久可以了并不用*2.

#include <iostream>
#include <algorithm>
#include <cmath>
#include <cstdio>
#include <cstring>
#include <vector>
using namespace std;
#define pb push_back
#define maxn 50000
struct node
{
    int v,l;
};
vector<node>g[maxn];
vector<int>dep;
int n,k,siz[maxn],nsiz,f[maxn],root,d[maxn],K,ans;
bool vis[maxn];
void getroot(int now,int fa)
{
    int v;
    siz[now]=1;f[now]=0;
    for(int i=0;i<g[now].size();i++)
    {
        if((v=g[now][i].v)!=fa && !vis[v])
        {
            getroot(v, now);
            siz[now]+=siz[v];
            f[now]=max(f[now],siz[v]);
        }
    }
    f[now]=max(f[now],nsiz-siz[now]);
    if (f[now]<f[root])root = now;
}
void getdep(int now,int fa)
{
    dep.pb(d[now]);
    int v;siz[now]=1;
    for(int i=0;i<g[now].size();i++)
    {
        v=g[now][i].v;
        if(v==fa||vis[v]) continue;
        d[v]=d[now]+g[now][i].l;
        getdep(v,now);
        siz[now]+=siz[v];
    }
}
int calc(int now,int have)
{
    dep.clear();d[now]=have;
    getdep(now,0);
    sort(dep.begin(),dep.end());
    int 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 now)
{
    int v;
    ans+=calc(now,0);
    vis[now]=1;
    for(int i=0;i<g[now].size();i++)
    {
        v=g[now][i].v;
        if(!vis[v])
        {
            ans-=calc(v,g[now][i].l);
            f[0]=nsiz=siz[v];
            getroot(v,root=0);
            work(root);
        }
    }
}
int main()
{
    int a,b,c;
    scanf("%d",&n);
    node tmp;
    for(int i=1;i<n;i++)
    {
        scanf("%d%d%d",&a,&b,&c);
        tmp.v=b;tmp.l=c;
        g[a].pb(tmp);
        tmp.v=a;tmp.l=c;
        g[b].pb(tmp);
    }
    scanf("%d",&K);
    f[0]=n;
    getroot(1,root=0);
    ans = 0;
    work(root);
    printf("%d\n", ans);
    return 0;
}


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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值