首页 > 其他分享 >点分治

点分治

时间:2024-08-14 15:05:45浏览次数:9  
标签:rt le int 分治 maxn mxsiz dis

点分治

以树的重心为根,对子树内的问题分治求解,时间复杂度可以做到 \(O(n\log n\times F(n))\),其中 \(F(n)\) 是解决经过根的问题所需要的处理

P3806 模版

给一棵有边权的树,多次询问树上是否存在距离为 \(k\) 的点对。

\(n\le 10^4,m\le 100,k\le 10^7\)

假设现在 \(rt\) 是根,则问题转化为求是否存在不在 \(rt\) 同一子树内的 \(u,v\) 使得 \(dis(rt,u)+dis(rt,v)=k\)。

转化一下式子得 \(dis(rt,u)=k-dis(rt,v)\),我们记 \(is[x]\) 表示是否出现过 \(x\),即对于每一个 \(u\),如果 \(is[k-dis(rt,v)]=1\),则说明有解;对于不在同一子树的限制,直接对于每个子树处理完再加入 \(is\) 就能避免。

时间复杂度 \(O(mn\log n)\)。

code
#include<bits/stdc++.h>
using namespace std;
const int maxn=1e4+3;
const int inf=0x3f3f3f3f;
int n,m;
struct edge{
    int v,w;
    edge(int v=0,int w=0): v(v),w(w){}
};
vector<edge>e[maxn];
int rt;
int siz[maxn],mxsiz[maxn];
int dis[maxn];
int quest[maxn];
vector<int>d;
queue<int>q;
bool isk[10000003];
bool vis[maxn];
bool ans[maxn];
int maxk,sum;
void add(int u,int v,int w){
    e[u].emplace_back(edge(v,w));
    e[v].emplace_back(edge(u,w));
}
void centroid(int u,int fa){
    siz[u]=1; mxsiz[u]=0;
    for(edge v:e[u]){
        if(v.v!=fa&&!vis[v.v]){
            centroid(v.v,u);
            siz[u]+=siz[v.v];
            mxsiz[u]=max(mxsiz[u],siz[v.v]);
        }
    }
    mxsiz[u]=max(mxsiz[u],sum-siz[u]);
    if(!rt||mxsiz[u]<mxsiz[rt]){
        rt=u;
    }
}
void distance(int u,int fa){
    d.emplace_back(dis[u]);
    for(edge v:e[u]){
        if(v.v!=fa&&!vis[v.v]){
            dis[v.v]=dis[u]+v.w;
            distance(v.v,u);
        }
    }

}

void dfz(int u,int fa){
    isk[0]=1;
    q.push(0);
    vis[u]=1;
    for(edge v:e[u]){
        if(v.v!=fa&&!vis[v.v]){
            dis[v.v]=v.w;
            distance(v.v,u);
            for(int i:d){
                for(int j=1;j<=m;j++){
                    if(quest[j]>=i){
                        ans[j]|=isk[quest[j]-i];
                    }
                }
            }
            while(!d.empty()){
                int i=d.back();
                if(i<=maxk){
                    q.push(i);
                    isk[i]=1;
                }
                d.pop_back();
            }
        }
    }
    while(!q.empty()){
        isk[q.front()]=0;
        q.pop();
    }
    for(edge v:e[u]){
        if(v.v!=fa&&!vis[v.v]){
            sum=siz[v.v];
            rt=0;
            mxsiz[rt]=inf;
            centroid(v.v,u);
            centroid(rt,0);
            dfz(rt,0);
        }
    }
}

signed main(){
    cin>>n>>m;
    for(int i=1,u,v,w;i<n;i++){
        cin>>u>>v>>w;
        add(u,v,w);
    }
    for(int i=1;i<=m;i++){
        cin>>quest[i];
        maxk=max(maxk,quest[i]);
    }
    sum=n;
    rt=0;
    mxsiz[rt]=inf;
    centroid(1,0);
    centroid(rt,0);
    dfz(rt,0);
    for(int i=1;i<=m;i++){
        if(ans[i]){
            cout<<"AYE\n";
        }else{
            cout<<"NAY\n";
        }
    }
    return 0; 
}

P4178 Tree

给一棵有边权的树,求树上距离 \(\le k\) 的点对数。

\(n\le 4\times 10^4,k\le 2\times 10^4\)

做法一:

思路同板子,同理转化一下式子得 \(dis(rt,u)\le k-dis(rt,v)\) 这个偏序问题非常经典,直接树状数组维护即可。

时间复杂度 \(O(n\log n\log k)\)。

code
#include<bits/stdc++.h>
using namespace std;
const int maxn=4e4+3;
const int inf=0x3f3f3f3f;
int n,m;
struct edge{
    int v,w;
    edge(int v=0,int w=0): v(v),w(w){}
};
vector<edge>e[maxn];
int rt;
int siz[maxn],mxsiz[maxn];
int dis[maxn];
int quest[maxn];
vector<int>d;
queue<int>q;
int tree[maxn];
bool vis[maxn];
int ans[maxn];
int maxk,sum;
void add(int u,int v,int w){
    e[u].emplace_back(edge(v,w));
    e[v].emplace_back(edge(u,w));
}
int lowbit(int x){return x&-x;}
void modify(int x,int c){
    x++;
    for(;x<=maxk+1;x+=lowbit(x)) tree[x]+=c;
}
int query(int x){
    x++;
    int ret=0;
    for(;x;x-=lowbit(x)) ret+=tree[x];
    return ret; 
}
void centroid(int u,int fa){
    siz[u]=1; mxsiz[u]=0;
    for(edge v:e[u]){
        if(v.v!=fa&&!vis[v.v]){
            centroid(v.v,u);
            siz[u]+=siz[v.v];
            mxsiz[u]=max(mxsiz[u],siz[v.v]);
        }
    }
    mxsiz[u]=max(mxsiz[u],sum-siz[u]);
    if(!rt||mxsiz[u]<mxsiz[rt]){
        rt=u;
    }
}
void distance(int u,int fa){
    d.emplace_back(dis[u]);
    for(edge v:e[u]){
        if(v.v!=fa&&!vis[v.v]){
            dis[v.v]=dis[u]+v.w;
            distance(v.v,u);
        }
    }

}

void dfz(int u,int fa){
    modify(0,1);
    q.push(0);
    vis[u]=1;
    for(edge v:e[u]){
        if(v.v!=fa&&!vis[v.v]){
            dis[v.v]=v.w;
            distance(v.v,u);
            for(int i:d){
                for(int j=1;j<=m;j++){
                    if(quest[j]>=i){
                        ans[j]+=query(quest[j]-i);
                    }
                }
            }
            while(!d.empty()){
                int i=d.back();
                if(i<=maxk){
                    q.push(i);
                    modify(i,1);
                }
                d.pop_back();
            }
        }
    }
    while(!q.empty()){
        modify(q.front(),-1);
        q.pop();
    }
    for(edge v:e[u]){
        if(v.v!=fa&&!vis[v.v]){
            sum=siz[v.v];
            rt=0;
            mxsiz[rt]=inf;
            centroid(v.v,u);
            centroid(rt,0);
            dfz(rt,0);
        }
    }
}

signed main(){
    cin>>n;
    for(int i=1,u,v,w;i<n;i++){
        cin>>u>>v>>w;
        add(u,v,w);
    }
    m=1;
    for(int i=1;i<=m;i++){
        cin>>quest[i];
        maxk=max(maxk,quest[i]);
    }
    sum=n;
    rt=0;
    mxsiz[rt]=inf;
    centroid(1,0);
    centroid(rt,0);
    dfz(rt,0);
    for(int i=1;i<=m;i++){
        cout<<ans[i]<<'\n';
    }
    return 0; 
}

做法二:

CF293E Close Vertices

给一棵有边权的树,求树上距离 \(\le k\),边数 \(\le l\) 的点对数。

\(l\le n\le 10^5,k\le 10^9\)

时间复杂度 \(O(n\log^2 n)\)。

标签:rt,le,int,分治,maxn,mxsiz,dis
From: https://www.cnblogs.com/DEV3937/p/18359030/dfz

相关文章

  • 点分治
    参考博客概述点分治适合处理大规模的树上路径信息问题。——摘自OIWiki时间复杂度\(O(n\logn)\)(每次\(calc\)时间复杂度为\(O(size_{root})\))。对于树上的所有路径及一个假定的根\(rt\),有两种路径:经过\(rt\)的;不经过\(rt\)的。第一种路径显然分两部分(可以......
  • cdq分治
    我觉得呢,cdq的本质就是在归并排序消掉一维的影响来处理多维偏序问题。既然本质跟二分有关,那很容易猜到cdq处理k维偏序的时间复杂度为\(O(Nlog^{k-1}N)\)三维偏序问题:形如:$求满足条件a_i<a_j,b_i<b_j,c_i<c_j且\(j!=i\)的j个数比较常考的就是三维偏序,一般做法就是sort消掉一......
  • CDQ分治
    P3810【模板】三维偏序(陌上花开)CDQ模板题,考虑先按\(a\)排序,减掉一位,然后再\(CDQ\)分治一维,用树状数组维护最后一维还有本题特殊,去重操作不要忘记点击查看代码#include<bits/stdc++.h>#definespeed()ios::sync_with_stdio(false),cin.tie(0),cout.tie(0);#definell......
  • CDQ分治
    CDQ分治的思想最早由IOI2008金牌得主陈丹琦在高中时整理并总结,它也因此得名。CDQ分治的思想常用于离线解决点对之间的问题,最经典的如三维偏序。解决这类问题的过程为:将序列$(l,r)$分为。递归处理序列$(l,mid)$和$(mid+1,r)$中的答案。设计算法处理......
  • cdq分治总结
    \(cdq\)分治是一种离线分治算法,可以将动态问题改变为静态问题,不适用于强制在线。其实现时通常将需要进行的操作存进一个结构体,然后对这些操作进行分治。打\(cdq\)分治时一个直观的感受就是很好想思路,但就是不知道怎么打。。。它一共有三个需要干的1找到范围中点\(mid\)......
  • 【CDQ分治】三元环
    三元环HDU-7439思路考虑\(3\)个点的有向图,要么成环,要么有一个点入度为\(2\),假设第个点的入度为\(d_i\),答案为\(C_n^3-\sum\limits_{i=1}^nC_{d_i}^2\)。根据题目关系,\(i\rightarrowj\)当且仅当\(i<j\and\f_i<f_j\and\g_i<g_j\),否则就是\(j\rightarrowi......
  • 【CDQ分治】【模板】三维偏序(陌上花开)
    P3810【模板】三维偏序(陌上花开)-洛谷|计算机科学教育新生态(luogu.com.cn)#include<bits/stdc++.h>usingnamespacestd;usingi64=longlong;template<typenameT>structBIT{#ifndeflowbit#definelowbit(x)(x&(-x));#endifintn;vector<T&......
  • 【CDQ分治】[P5094 [USACO04OPEN] MooFest G 加强版
    P5094[USACO04OPEN]MooFestG加强版-洛谷|计算机科学教育新生态(luogu.com.cn)#include<bits/stdc++.h>usingnamespacestd;usingi64=longlong;intmain(){ios::sync_with_stdio(false);cin.tie(nullptr);intn;cin>>n;vecto......
  • [算法] 一些分治
    普通分治其实没啥,每次只计算跨越分治中心的区间的贡献,剩下的递归到左右两边进行分治;时间复杂度:分治树高度为$\Theta(\logn)$,乘上其他操作的复杂度即可;例题一:现在有一个$n$阶排列$a$,计算:\[\sum^{n}_{i=1}\sum^{n}_{j=i}\min(a_i,a_{i+1},...,a_j)\]......
  • 20240806:点分治,虚树选做
    POJ-1741Tree题意:给定一棵树,求多少无序对\((u,v)\)满足\(\text{dist}(u,v)\lek\)。对于树上的路径进行分类:经过根;不经过根。第二类路径点可以通过递归子树求出。对于经过根的路径,可以一遍dfs求出每个点到根的距离\(\text{dis}(u)\)。问题转化为求\(\text{dis}(u)......