点分治

poj1741树上的点对

题目大意:给定一棵树,求树上距离不超过k的点对个数。

思路:点分治模板题。

#include<iostream>

#include<cstdio>

#include<algorithm>

#include<cstring>

#include<vector>

#define maxnode 10005

#define inf 2100000000LL

using namespace std;

int point[maxnode]={0},next[maxnode*2]={0},en[maxnode*2]={0},va[maxnode*2]={0},

    siz[maxnode]={0},tot=0,maxn,root,ans=0,k,n;

bool visit[maxnode]={0};

vector<int> dis;

void add(int u,int v,int w)

{

    ++tot;next[tot]=point[u];point[u]=tot;en[tot]=v;va[tot]=w;

    ++tot;next[tot]=point[v];point[v]=tot;en[tot]=u;va[tot]=w;

}

void getroot(int u,int fa,int nn)

{

    int i,maxsiz=0,j;

    siz[u]=1;

    for (i=point[u];i;i=next[i])

    {

        if (!visit[j=en[i]]&&j!=fa)

        {

            getroot(j,u,nn);siz[u]+=siz[j];

            maxsiz=max(maxsiz,siz[j]);

        }

    }

    maxsiz=max(maxsiz,nn-siz[u]);

    if (maxsiz<maxn)

    {

        maxn=maxsiz;root=u;

    }

}

void getdep(int u,int dep,int fa)

{

    int i;

    dis.push_back(dep);siz[u]=1;

    for (i=point[u];i;i=next[i])

        if (en[i]!=fa&&!visit[en[i]]) 

        {

          getdep(en[i],dep+va[i],u);siz[u]+=siz[en[i]];

        }

}

int cal(int u,int dep)

{

    int sum=0,i,j;    

    dis.clear();getdep(u,dep,0);

    sort(dis.begin(),dis.end());

    for (i=0,j=dis.size()-1;i<j;)

    {

        if (dis[i]+dis[j]<=k) 

        {

          sum+=j-i;++i;

        }

        else --j;

    }

    return sum;

}

void work(int u)

{

    int i,j;

    ans+=cal(u,0);visit[u]=true;

    for (i=point[u];i;i=next[i])

    {

        if (!visit[en[i]])

        {

            ans-=cal(en[i],va[i]);

            maxn=siz[en[i]];

            getroot(en[i],root=0,siz[en[i]]);

            work(root);

        }

    }

}

int main()

{

    freopen("poj1741_tree.in","r",stdin);

    freopen("poj1741_tree.out","w",stdout);

    

    int i,j,u,v,w;

    while(scanf("%d%d",&n,&k)==2)

    {

        if (n==0&&k==0) break;

        tot=0;memset(visit,false,sizeof(visit));

        memset(point,0,sizeof(point));

        for (i=1;i<n;++i)

        {

            scanf("%d%d%d",&u,&v,&w);

            add(u,v,w);

        }

        maxn=n;ans=0;

        getroot(1,root=0,n);

        work(root);

        printf("%d\n",ans);

    }

}
View Code

 

hdu4670Cube number on a tree 
题目大意:给定一棵树,每个点都有一个权值,可以分为一些给定质数的和(质数个数不超过30),求出一些路径的条数,使路径都满足路上点权乘积是个立方数。 
思路:我们对于每一个点都分解质因数,保存下每种的个数,满足条件的路径就是那些点的相应因数个数相加后%3=0的路径了。 
点分,对每一个点统计路径个数,减去重复的就可以了。 
对于统计答案的部分,我们dfs求出到点到重心路径上的和,把这个30位的数组转成一个三进制数,放到map里。穷举每一个map里面的数,找到互补的数,贡献给答案就可以了。这里注意如果这个数和互补数一样的话,ans的计算方式略有不同。 
对于重复的部分,我们减去的时候要时刻注意变量的含义。 
这道题目的思维和代码量都中等,但是结合起来莫名的很难写(蒟蒻的吐槽),要认真思考好每一个部分的实现。 

#pragma comment(linker,"/STACK:1024000000,1024000000")

#include<iostream>

#include<cstdio>

#include<cstring>

#include<algorithm>

#include<map>

#define maxnode 50005

#define inf 2100000000LL

#define sta 30

using namespace std;

struct use{

    int num[35];

    void init()

    {

        int i;

        for (i=0;i<35;++i) num[i]=0;

    }

}ai[maxnode]={0};

int prime[35]={0},point[maxnode]={0},nex[maxnode*2]={0},en[maxnode*2]={0},tot=0,

    root,son[maxnode]={0},siz[maxnode]={0};

long long ans=0,mi[35]={0};

bool visit[maxnode]={false};

map <long long,long long> cnt;

void add(int u,int v)

{

    ++tot;nex[tot]=point[u];point[u]=tot;en[tot]=v;

    ++tot;nex[tot]=point[v];point[v]=tot;en[tot]=u;

}

void fp(int j,long long x)

{

    int i;

    ai[j].init();

    for (i=1;i<=prime[0];++i)

    {

        while(x!=0&&x%prime[i]==0)

        {

            x/=prime[i];++ai[j].num[i];

            if (ai[j].num[i]>=3) ai[j].num[i]%=3;

        }

    }

}

long long threezten(use x)

{

    int i,j;

    long long sum=0;

    for (i=1;i<=sta;++i)

        sum+=mi[i-1]*(x.num[i]%3);

    return sum;

}

use tenzthree(long long x)

{

    int i,j;

    use y;

    y.init();i=1;

    while(x)

    {

        y.num[i]=x%3;x/=3;++i;

    }

    return y;

}

use jia(use x,use y)

{

    int i;

    for (i=1;i<=sta;++i) x.num[i]=(x.num[i]+y.num[i])%3;

    return x;

}

use jian(use x,use anc)

{

    int i;

    use y;

    for (i=1;i<=sta;++i) y.num[i]=(3-x.num[i]+anc.num[i])%3;

    return y;

}

bool equal(use x,use y)

{

    int i;

    for (i=1;i<=sta;++i) if (x.num[i]!=y.num[i]) return false;

    return true;

}

void getroot(int u,int fa,int up)

{

    int i,j;

    siz[u]=1;son[u]=0;

    for (i=point[u];i;i=nex[i])

    {

        if ((j=en[i])!=fa&&!visit[j])

        {

            getroot(j,u,up);siz[u]+=siz[j];

            son[u]=max(son[u],siz[j]);

        }

    }

    son[u]=max(son[u],up-siz[u]);

    if (son[u]<son[root]) root=u;

}

void getdis(int u,int fa,use dis)

{

    int i,j;

    long long kk;

    if (!cnt.count(kk=threezten(dis))) cnt[kk]=0;

    ++cnt[kk];siz[u]=1;

    for (i=point[u];i;i=nex[i])

    {

        if ((j=en[i])!=fa&&!visit[j])

        {

            getdis(j,u,jia(dis,ai[j]));siz[u]+=siz[j];

        }

    }

}

long long calc(int u,use cc,use anc)

{

    int i,j;

    long long sum=0,kk;

    use x,y;

    cnt.clear();

    getdis(u,0,jia(cc,ai[u]));

    map<long long,long long>::iterator it;

    for (it=cnt.begin();it!=cnt.end();++it)

    {

        if (it->second!=0)

        {

            x=tenzthree(it->first);y=jian(x,anc);

            if (cnt.count(kk=threezten(y)))

            {

                if (equal(x,y)) sum+=(cnt[kk]+cnt[kk]*(cnt[kk]-1)/2);

                else sum+=((it->second)*(cnt[kk]));

                cnt[kk]=0;it->second=0;

            }

        }

    }

    return sum;

}

void work(int u)

{

    int i,j;

    use kk;

    kk.init();visit[u]=true;

    ans+=calc(u,kk,ai[u]);

    for (i=point[u];i;i=nex[i])

    {

        if (!visit[j=en[i]])

        {

            ans-=calc(j,ai[u],ai[u]);

            root=0;son[root]=inf;

            getroot(j,0,siz[j]);

            work(root);

        }

    }

}

int main()

{

    int n,i,j,k,u,v;

    long long aa;

    mi[0]=1;

    for (i=1;i<=sta;++i) mi[i]=mi[i-1]*3;

    while(scanf("%d%d",&n,&prime[0])==2)

    {

      ans=tot=0;

      memset(point,0,sizeof(point));

      memset(nex,0,sizeof(nex));

      memset(visit,false,sizeof(visit));

      for (i=1;i<=prime[0];++i) scanf("%d",&prime[i]);

      for (i=1;i<=n;++i)

      {

        scanf("%I64d",&aa);fp(i,aa);

      }

      for (i=1;i<n;++i)

       {

        scanf("%d%d",&u,&v);add(u,v);

      }

      root=0;son[root]=inf;getroot(1,0,n);

      work(root);

      printf("%I64d\n",ans);

    }

}
View Code

 

bzoj2152聪聪可可

题目大意:给定一棵树,求边权和是三的倍数的路径数(同一个点和正反路径都算不同的路径),以分数的形式表示出来(分母是总的路径数n^2)。

思路:点分,用0、1、2为下标保存路径个数,然后相应的乘起来就可以了。

#include<iostream>

#include<cstdio>

#include<cstring>

#include<algorithm>

#define maxedge 20005

#define maxnode 20005

using namespace std;

int point[maxedge*2]={0},next[maxedge*2]={0},en[maxedge*2]={0},siz[maxnode]={0},va[maxedge*2]={0},

    ans=0,root,maxn,tot=0,zhan[3]={0},dis[maxnode]={0};

bool visit[maxnode]={false};

int gcd(int x,int y)

{

    if (!y) return x;

    else return gcd(y,x%y);

}

void add(int x,int y,int w)

{

    ++tot;next[tot]=point[x];point[x]=tot;en[tot]=y;va[tot]=w;

    ++tot;next[tot]=point[y];point[y]=tot;en[tot]=x;va[tot]=w;

}

void getroot(int u,int fa,int al)

{

    int i,j,maxsiz=0;  siz[u]=1;

    for (i=point[u];i;i=next[i])

    {

        if ((j=en[i])!=fa&&!visit[j])

        {

            getroot(j,u,al);siz[u]+=siz[j];

            maxsiz=max(maxsiz,siz[j]);

        }

    }

    maxsiz=max(maxsiz,al-siz[u]);

    if (maxsiz<maxn){maxn=maxsiz;root=u;}

}

void getdep(int u,int fa,int dep)

{

    int i,j; dis[u]=dep;siz[u]=1;++zhan[dep];

    for (i=point[u];i;i=next[i])

    {

        if (!visit[j=en[i]]&&j!=fa)

        {

            getdep(j,u,(dep+va[i])%3);siz[u]+=siz[j];

        }

    }

}

int calc(int u,int al)

{

    int i,j,sum=0;

    memset(zhan,0,sizeof(zhan));

    getdep(u,0,al);

    sum+=zhan[0]*zhan[0]+2*zhan[1]*zhan[2];

    return sum;

}

void work(int u)

{

    int i,j;

    ans+=calc(u,0);visit[u]=true;

    for (i=point[u];i;i=next[i])

    {

        if (!visit[j=en[i]])

        {

            ans-=calc(j,va[i]%3);maxn=siz[j];

            getroot(j,root=0,siz[j]);

            work(root);

        }

    }

}

int main()

{

    int i,j,n,x,y,w,mu,k;

    scanf("%d",&n);mu=n*n;

    for (i=1;i<n;++i)

    {

        scanf("%d%d%d",&x,&y,&w);

        add(x,y,w);

    }

    maxn=n;getroot(1,root=0,n);work(root);

    k=gcd(mu,ans);mu/=k;ans/=k;

    printf("%d/%d\n",ans,mu);

}
View Code

 

bzoj4016最短路径树问题

题目大意:给定一个无向图,求最短路径树(从1开始且每个点到根的路径字典序最小),然后求最短路径树上的经过k个点的最长路径以及经过k个点的长度为最长路径长度的路径个数。

思路:用点分,在更新最长路径的时候更新一下方案数,注意不能简单的+1-1,而是一些累加。

         一开始理解错了题意(理解成了路径长度为最长路径长度,但经过点的个数不一定是k的路径个数),写了两个点分wa+tle。。。

#include<iostream>

#include<cstdio>

#include<cstring>

#include<algorithm>

#include<map>

#include<vector>

#define maxnode 30005

#define maxedge 60005

#define len 100000

using namespace std;

struct use{

    int dian,dx;

};

int poi[maxnode]={0},nex[maxedge*2]={0},enn[maxedge*2]={0},vaa[maxedge*2]={0},to=0,k,dis[maxnode]={0},que[len+5]={0},ans=0,root={0},

    point[maxnode]={0},next[maxedge*2]={0},en[maxedge*2]={0},va[maxedge*2]={0},tot=0,siz[maxnode]={0},maxn,n,kk=0,zhan[maxnode][2]={0};

bool visit[maxnode]={false},flag[maxedge*2]={false};

void add1(int x,int y,int w)

{

    ++to;nex[to]=poi[x];poi[x]=to;enn[to]=y;vaa[to]=w;

    ++to;nex[to]=poi[y];poi[y]=to;enn[to]=x;vaa[to]=w;

}

void add(int x,int y,int w)

{

    ++tot;next[tot]=point[x];point[x]=tot;en[tot]=y;va[tot]=w;

    ++tot;next[tot]=point[y];point[y]=tot;en[tot]=x;va[tot]=w;

}

void spfa()

{

    int x,y,i,j,tail,head;

    memset(dis,127,sizeof(dis));dis[1]=0;visit[1]=true;

    head=tail=0;que[++tail]=1;

    while(head!=tail)

    {

        head=head%len+1;

        x=que[head];visit[x]=false;

        for (y=poi[x];y;y=nex[y])

        {

            if (dis[enn[y]]>dis[x]+vaa[y])

            {

                dis[enn[y]]=dis[x]+vaa[y];

                if (!visit[enn[y]])

                {

                    visit[enn[y]]=true;tail=tail%len+1;

                    que[tail]=enn[y];

                }

            }

        }

    }

    for (i=1;i<=n;++i)

        for (j=poi[i];j;j=nex[j])

            if (dis[enn[j]]==dis[i]+vaa[j]) flag[j]=true;

}

int my_comp(const use x,const use y)

{

    if (x.dian<y.dian) return 1;

    else return 0;

}

void build(int u,int fa,int dd)

{

    int i,j;

    vector<use> cc;

    use xx;

    cc.clear();visit[u]=true;

    if (fa) add(fa,u,dd);

    for (i=poi[u];i;i=nex[i])

      if (flag[i]) {xx.dian=enn[i];xx.dx=vaa[i];cc.push_back(xx);}

    sort(cc.begin(),cc.end(),my_comp);

    for (i=0;i<cc.size();++i)

        if (!visit[cc[i].dian]) build(cc[i].dian,u,cc[i].dx);

}

void getroot(int u,int fa,int al)

{

    int i,j,maxsiz=0; siz[u]=1;

    for (i=point[u];i;i=next[i])

    {

        if (!visit[j=en[i]]&&j!=fa)

        {

            getroot(j,u,al);siz[u]+=siz[j];

            maxsiz=max(maxsiz,siz[j]);

        }

    }

    maxsiz=max(maxsiz,al-siz[u]);

    if (maxsiz<maxn){root=u;maxn=maxsiz;}

}

void getdep(int u,int fa,int dep,int dd)

{

    int i,j;

    dis[u]=dep;

    if (k-1-dd>=0)

    {

      if ((j=dis[u]+zhan[k-1-dd][0])>=kk)

      {

        if (j==kk) ans+=zhan[k-1-dd][1];

        else {ans=zhan[k-1-dd][1];kk=j;}

      }

    }

    for (i=point[u];i;i=next[i])

        if (!visit[j=en[i]]&&j!=fa)  getdep(j,u,dep+va[i],dd+1);

}

void ins(int u,int fa,int dep,int dd)

{

    int i,j;

    siz[u]=1;dis[u]=dep;

    if (zhan[dd][0]<=dis[u])

    {

        if (zhan[dd][0]<dis[u]) {zhan[dd][0]=dis[u];zhan[dd][1]=1;}

        else ++zhan[dd][1];

    }

    for (i=point[u];i;i=next[i])

        if (!visit[j=en[i]]&&j!=fa)

        {

            ins(j,u,dep+va[i],dd+1);siz[u]+=siz[j];

        }

}

void workk(int u)

{

     int i,j,x,y;

     visit[u]=true;memset(zhan,0,sizeof(zhan));zhan[0][0]=0;zhan[0][1]=1;

     for (i=point[u];i;i=next[i])

     {

         if(!visit[j=en[i]]){getdep(j,0,va[i],1);ins(j,0,va[i],1);}

     }

     for (i=point[u];i;i=next[i])

     {

        if (!visit[j=en[i]])

        {

            maxn=siz[j];getroot(j,root=0,siz[j]);workk(root);

        }

     }

}

void getk()

{

    int i,j,x,y;

    memset(visit,false,sizeof(visit));

    maxn=n;getroot(1,root=0,n);workk(root);

}

int main()

{

    int m,i,j,t,x,y,w;

    scanf("%d%d%d",&n,&m,&k);

    for (i=1;i<=m;++i)

    {

        scanf("%d%d%d",&x,&y,&w);

        add1(x,y,w);

    }

    spfa();memset(visit,false,sizeof(visit));

    build(1,0,0);getk();

    printf("%d %d\n",kk,ans);

}
View Code

 

你可能感兴趣的:(点分治)