[BZOJ2599][IOI2011]Race(点分治)

来源:互联网 发布:志鸿优化系列丛书 编辑:程序博客网 时间:2024/06/05 18:55

题目描述

传送门

题解

同样是树上的路径,点分的思路是很显然的。不过需要同时满足两个条件。
观察可以发现路径长度=k可以作为点分判断的条件,那么我们需要把最小的边数转化成判断存在性问题。
每一次遍历等于是给出了一些点,每个点有权,问权值和=k的点对数量。那么显然排序后又可以用两个指针lr扫出来。时间复杂度O(nlogn)
最后从小向大扫就可以了。总体时间复杂度O(nlog2n)

代码

#include<algorithm>#include<iostream>#include<cstring>#include<cstdio>using namespace std;#define N 200005#define INF 2000000000int n,k,x,y,z,sum,root,cnt;int tot,point[N],nxt[N*2],v[N*2],c[N*2];int size[N],big[N],dis[N],deep[N],ans[N];struct hp{int dis,deep;}pt[N];bool vis[N];void addedge(int x,int y,int z){    ++tot; nxt[tot]=point[x]; point[x]=tot; v[tot]=y; c[tot]=z;    ++tot; nxt[tot]=point[y]; point[y]=tot; v[tot]=x; c[tot]=z;}void getroot(int x,int fa){    size[x]=1; big[x]=0;    for (int i=point[x];i;i=nxt[i])        if (v[i]!=fa&&!vis[v[i]])        {            getroot(v[i],x);            size[x]+=size[v[i]];            big[x]=max(big[x],size[v[i]]);        }    big[x]=max(big[x],sum-size[x]);    if (big[x]<big[root]) root=x;}void getdeep(int x,int fa){    pt[++cnt].dis=dis[x],pt[cnt].deep=deep[x];    for (int i=point[x];i;i=nxt[i])        if (v[i]!=fa&&!vis[v[i]])        {            dis[v[i]]=dis[x]+c[i];            deep[v[i]]=deep[x]+1;            getdeep(v[i],x);        }}int cmp(hp a,hp b){    return a.dis<b.dis;}void calc(int x,int nowdis,int nowdeep,int add){    dis[x]=nowdis; deep[x]=nowdeep; cnt=0;    getdeep(x,0);    sort(pt+1,pt+cnt+1,cmp);    for (int l=1,r=cnt;l<=r;l++)    {        while (l<r&&pt[l].dis+pt[r].dis>k) r--;        for (int i=r;pt[l].dis+pt[i].dis==k;i--) ans[pt[l].deep+pt[i].deep]+=add;    }}void dfs(int x){    calc(x,0,0,1);    vis[x]=true;    for (int i=point[x];i;i=nxt[i])        if (!vis[v[i]])        {            calc(v[i],c[i],1,-1);            sum=size[v[i]]; root=0;            getroot(v[i],0);            dfs(root);        }}int main(){    scanf("%d%d",&n,&k);    for (int i=1;i<n;++i)    {        scanf("%d%d%d",&x,&y,&z);        x++; y++;        addedge(x,y,z);    }    sum=n; root=0; big[0]=INF;    getroot(1,0);    dfs(root);    for (int i=1;i<=n;++i)        if (ans[i])        {            printf("%d\n",i);            return 0;        }    puts("-1");}

总结

①求最小/最大值问题可以转化为判断存在性问题。并且在点分中的加加减减比较好用。

0 0
原创粉丝点击