点分治专题

点分治详细解析

【点分治】的学习笔记和众多例题

P3806 【模板】点分治1

求树上最小距离等于K的点对是否存在

离线+点分治

题解

#include<bits/stdc++.h>
using namespace std;
const int MAX=1e4+5;
const int INF=1e7;
int n,m,K;
struct P
{
    int to,nxt,v;
    void is(int x1,int x2,int x3){to=x1;nxt=x2;v=x3;}
}e[MAX<<1];
int head[MAX],sz[MAX],dis[MAX],maxp[MAX],rem[MAX],test[MAX],query[MAX],judge[INF],q[MAX],cnt,sum,rt;
bool vis[MAX];
void init()
{
    cnt=0;
    memset(head,-1,sizeof(head));
}
void adde(int u,int v,int w)
{
    e[cnt].is(v,head[u],w);head[u]=cnt++;
    e[cnt].is(u,head[v],w);head[v]=cnt++;
}
void getrt(int u,int pa)
{
    sz[u]=1; maxp[u]=0;
    for(int i=head[u];i!=-1;i=e[i].nxt)
    {
        int to=e[i].to;
        if(to==pa||vis[to]) continue;
        getrt(to,u);
        sz[u]+=sz[to];//update the number of subnode in root [u],[to] is [u]‘s subnode
        maxp[u]=max(maxp[u],sz[to]);//update the max of root [u],if sz [to] is greater
    }
    maxp[u]=max(maxp[u],sum-sz[u]);//in_ex
    if(maxp[u]<maxp[rt]) rt=u;
}
void getdis(int u,int fa)
{
    rem[++rem[0]]=dis[u];
    for(int i=head[u];i!=-1;i=e[i].nxt)
    {
        int to=e[i].to;
        if(to==fa||vis[to])continue;
        dis[to]=dis[u]+e[i].v;
        getdis(to,u);
    }
}
void calc(int u)
{
    int p=0;
    for(int i=head[u];i!=-1;i=e[i].nxt)
    {
        int to=e[i].to;
        if(vis[to])continue;
        rem[0]=0; dis[to]=e[i].v;
        getdis(to,u);//处理u的每个子树的dis
        for(int j=rem[0];j;--j)//遍历当前子树的dis
        for(int k=1;k<=m;++k)//遍历每个询问
        if(query[k]>=rem[j])
        test[k]|=judge[query[k]-rem[j]];
        //如果query[k]-rem[j]d的路径存在就标记第k个询问
        for(int j=rem[0];j;--j)//保存出现过的dis于judge
        q[++p]=rem[j],judge[rem[j]]=1;
    }
    for(int i=1;i<=p;++i)//处理完这个子树就清空judge
    judge[q[i]]=0;//特别注意一定不要用memeset,会T
}
void divide(int u)
{
    //judge[i]表示到根距离为i的路径是否存在
    vis[u]=1;judge[0]=1; calc(u);//处理以u为根的子树
    for(int i=head[u];i!=-1;i=e[i].nxt)//对每个子树进行分治
    {
        int to=e[i].to;
        if(vis[to])continue;
        sum=sz[to]; maxp[rt=0]=INF;
        getrt(to,0); divide(rt);//在子树中找重心并递归处理
    }
}
int main()
{
    int x,y,z;
    init();
    scanf("%d%d",&n,&m);
    for(int i=1;i<n;++i)
    {
        scanf("%d%d%d",&x,&y,&z);
        adde(x,y,z);
    }
    for(int i=1;i<=m;++i)scanf("%d",&query[i]);
    maxp[rt]=sum=n;
    getrt(1,0);
    divide(rt);
    for(int i=1;i<=m;++i) printf("%s\n",test[i]?"AYE":"NAY");
    return 0;
}

POJ1741 Tree

求树上最小距离小于m的点对个数

#include<stdio.h>
#include<string.h>
#include<algorithm>
using namespace std;
const int MAX=1e4+5;
int n,m;
struct P
{
    int to,nxt,v;
    void is(int x1,int x2,int x3){to=x1;nxt=x2;v=x3;}
}e[MAX<<1];
int head[MAX],sz[MAX],dis[MAX],maxp[MAX],rem[MAX],cnt,sum,rt,ans;
bool vis[MAX];
void init()
{
    cnt=ans=rt=0;rem[0]=0;
    memset(head,-1,sizeof(head));
    memset(vis,0,sizeof(vis));
}
void adde(int u,int v,int w)
{
    e[cnt].is(v,head[u],w);head[u]=cnt++;
    e[cnt].is(u,head[v],w);head[v]=cnt++;
}
void getrt(int u,int pa)
{
    sz[u]=1; maxp[u]=0;
    for(int i=head[u];i!=-1;i=e[i].nxt)
    {
        int to=e[i].to;
        if(to==pa||vis[to]) continue;
        getrt(to,u);
        sz[u]+=sz[to];//update the number of subnode in root [u],[to] is [u]‘s subnode
        maxp[u]=max(maxp[u],sz[to]);//update the max of root [u],if sz [to] is greater
    }
    maxp[u]=max(maxp[u],sum-sz[u]);//in_ex
    if(maxp[u]<maxp[rt]) rt=u;
}
void getdis(int u,int fa)
{
    rem[++rem[0]]=dis[u];
    for(int i=head[u];i!=-1;i=e[i].nxt)
    {
        int to=e[i].to;
        if(to==fa||vis[to])continue;
        dis[to]=dis[u]+e[i].v;
        getdis(to,u);
    }
}
int cal(int u,int ct)
{
    dis[u]=ct;rem[0]=0;
    getdis(u,0);
    sort(rem+1,rem+rem[0]+1);
    int l=1,r=rem[0],x=0;
    while(l<r)
    {
        if(rem[l]+rem[r]<=m) {x+=r-l;++l;}
        else --r;
    }
    return x;
}
int divide(int u)
{
    vis[u]=1;ans+=cal(u,0);//处理以u为根的子树
    for(int i=head[u];i!=-1;i=e[i].nxt)//对每个子树进行分治
    {
        int to=e[i].to;
        if(vis[to])continue;
        ans-=cal(to,e[i].v);
        sum=sz[to];rt=0;
        getrt(to,0); divide(rt);//在子树中找重心并递归处理
    }
    return ans;
}
int main()
{
    int x,y,z;
    while(~scanf("%d%d",&n,&m))
    {
        if(n==0&&m==0) break;
        init();
        for(int i=1;i<n;++i)
        {
            scanf("%d%d%d",&x,&y,&z);
            adde(x,y,z);
        }
        maxp[rt]=sum=n;
        getrt(1,0);
        printf("%d\n",divide(rt));
    }
    return 0;
}

求点对最小距离等于m 的数量

int cal_(int u,int ct)
{
    dis[u]=ct;rem[0]=0;
    getdis(u,0);
    sort(rem+1,rem+rem[0]+1);
    int r=rem[0],x=0;
    for(int l=1;l<r;++l)
    {
        while(rem[l]+rem[r]==m) ++x;
        --r;
    }
    return x;
}

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值