点分治

来源:互联网 发布:女明星 瘦 知乎 编辑:程序博客网 时间:2024/04/30 02:35

什么题也写不动就学学这种简单的知识点

首先点分治其实也叫树分治
就是对树进行分治
正如我们每次从序列的mid分开进行分治一样
我们每次从树的重心将树分成几个联通块,在这几个联通快中找重心分治
思路很朴素

普遍的做法是
找重心
然后处理每个子树对根节点的贡献
然后减去每个子树内的重复贡献
子树重复上述操作

举例
来看树分治的裸题
给一颗树求问有多少对点它们两者间的距离小于等于K
做法类比上面的步骤就是
找重心(这个不会你还是先好好写写树形dp吧)
dfs一遍求出所有点到当前的重心的距离,排序,然后求出有多少对的和小于等于k
很显然同一颗子树内的点他们之间的路径并不等于到重心的距离加起来,所以我们要减去。
然后我们发现显然,通过重心的路径我们已经全部统计过了,所以我们可以把这个点删去。然后在其余的子树中重复进行上述操作。
因为我们选的是重心所以树的规模每次都会减小一半。

上模板 bzoj3365
这个题输入里面给的那个字母是没用的

#include<cstdio>#include<cstring>#include<utility>#include<algorithm>#include<iostream>#include<queue>#include<stack>#include<cmath>#include<cstdlib>#include<ctime>using namespace std;inline int read(){    char ch='*';    int f=1;    while(!isdigit(ch=getchar())) if(ch=='-') f=-1;    int num=ch-'0';    while(isdigit(ch=getchar())) num=num*10+ch-'0';    return num*f;}#define N 50010#define inf 0x7fffffffstruct edge{    int to,next,val;}e[N*2];int h[N],n;int son[N],f[N];bool vis[N];int d[N],deep[N];int sum,m,root,k,ans,cnt;inline void add(int from,int to,int val){    e[++cnt].next=h[from];    e[cnt].to=to;    e[cnt].val=val;    h[from]=cnt;    return ;}void getroot(int v,int fa){    son[v]=1;f[v]=0;    for(register int i=h[v];i;i=e[i].next)    {        if(e[i].to!=fa&&!vis[e[i].to])        {            getroot(e[i].to,v);            son[v]+=son[e[i].to];            f[v]=max(f[v],son[e[i].to]);        }    }    f[v]=max(f[v],sum-son[v]);    if(f[v]<f[root]) root=v;    return ;}void getdeep(int x,int fa){    deep[++deep[0]]=d[x];    for(register int i=h[x];i;i=e[i].next)    {        if(e[i].to!=fa&&!vis[e[i].to])        {            d[e[i].to]=d[x]+e[i].val;            getdeep(e[i].to,x);        }    }    return ;}inline int cal(int x,int cost){    d[x]=cost;    deep[0]=0;    getdeep(x,0);    sort(deep+1,deep+deep[0]+1);    int l=1,r=deep[0],sum=0;    while(l<r)    {        if(deep[l]+deep[r]<=k){            if(l+r==k)            sum++;            l++;        }        else r--;    }    return sum;}void solve(int x){    ans+=cal(x,0);    vis[x]=1;    for(register int i=h[x];i;i=e[i].next)    {        if(!vis[e[i].to])        {            ans-=cal(e[i].to,e[i].val);            sum=son[e[i].to];            root=0;            getroot(e[i].to,0);            //cerr<<root<<endl;            solve(root);        }    }}char ch[2];int main(){    int u,v,w;    n=read();m=read();    for(register int i=1;i<=m;i++)    {        u=read();v=read();w=read();        scanf("%s",ch);        add(u,v,w);        add(v,u,w);    }    k=read();    f[0]=inf;    sum=n;    getroot(1,0);    //cerr<<root;    solve(root);    printf("%d\n",ans);    return 0;}

bzoj1468买一送一
不需要读m和后面的字母随便改一下就过了
没想到这是男人八题之一

bzoj2599ioi race
给一棵树,每条边有权.求一条简单路径,权值和等于K,且边的数量最小.N <= 200000, K <= 1000000
这道题看起来很难其实就是一道sb题
我们多记录一个每个节点到当前根节点要走多少条边
对答案开个ans【1000000】,把所有可能的都更新到然后最后扫一遍就好。
注意统计答案时=k的话不能用单调指针直接扫
要大力循环
其实也是水题

#include<cstdio>#include<cstring>#include<utility>#include<algorithm>#include<iostream>#include<queue>#include<stack>#include<cmath>#include<cstdlib>#include<ctime>using namespace std;inline int read(){    char ch='*';    int f=1;    while(!isdigit(ch=getchar())) if(ch=='-') f=-1;    int num=ch-'0';    while(isdigit(ch=getchar())) num=num*10+ch-'0';    return num*f;}#define N 200010#define inf 0x7fffffffstruct edge{    int to,next,val;}e[N*2];int ans[N];int h[N],n;int son[N],f[N];bool vis[N];int d[N];int sum,m,root,k,cnt;int dis[N];struct q{    int d,dis;}deep[N];int tot;inline void add(int from,int to,int val){    e[++cnt].next=h[from];    e[cnt].to=to;    e[cnt].val=val;    h[from]=cnt;    return ;}void getroot(int v,int fa){    son[v]=1;f[v]=0;    for(register int i=h[v];i;i=e[i].next)    {        if(e[i].to!=fa&&!vis[e[i].to])        {            getroot(e[i].to,v);            son[v]+=son[e[i].to];            f[v]=max(f[v],son[e[i].to]);        }    }    f[v]=max(f[v],sum-son[v]);    if(f[v]<f[root]) root=v;    return ;}void getdeep(int x,int fa){    deep[++tot].d=d[x],deep[tot].dis=dis[x];    for(register int i=h[x];i;i=e[i].next)    {        if(e[i].to!=fa&&!vis[e[i].to])        {            d[e[i].to]=d[x]+e[i].val;            dis[e[i].to]=dis[x]+1;            getdeep(e[i].to,x);        }    }    return ;}inline bool  cmp(q x,q y){    return x.d<y.d;}inline void cal(int x,int cost,int cost2,int add){    d[x]=cost;    dis[x]=cost2;    tot=0;    getdeep(x,0);    sort(deep+1,deep+tot+1,cmp);    int l=1,r=tot,sum=0;    for(l=1,r=tot;l<=r;l++)    {        while(l<r&&deep[l].d+deep[r].d>k)r--;        for(int i=r;deep[l].d+deep[i].d==k;i--)            ans[deep[l].dis+deep[i].dis]+=add;    }//  return sum;}void solve(int x){    cal(x,0,0,1);    vis[x]=1;    for(register int i=h[x];i;i=e[i].next)    {        if(!vis[e[i].to])        {            cal(e[i].to,e[i].val,1,-1);            sum=son[e[i].to];            root=0;            getroot(e[i].to,0);            //cerr<<root<<endl;            solve(root);        }    }}char ch[2];int main(){    int u,v,w;    n=read();    k=read();    for(register int i=1;i<n;i++)    {        u=read();v=read();w=read();        //scanf("%s",ch);        u++,v++;        add(u,v,w);        add(v,u,w);    }    f[0]=inf;    sum=n;    root=0;    getroot(1,0);    //cerr<<root;    solve(root);    for(register int i=1;i<=n;i++)    {        if(ans[i]) {            printf("%d\n",i);            return 0;        }    }    puts("-1");    return 0;}

跑的略慢

原创粉丝点击