点分治

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

 

bzoj1758 重建计划

题目大意:求树上长度在l~u的链的sigma va[i]/|s|的最大值。

思路:分数规划+分治+单调队列。分数规划之后就是求sigma va[i]-mid的最长链是否>=0,点分治之后,用单调队列维护深度递减的最大值,然后更新最大值。

有一些优化:1)点分治里分数规划会使下界变优;

      2)二分的时候,mid=(l*9+r)/10,会快很多。

      3)点分每个u算答案的时候,要用儿子的siz/dep从小到大排序之后算,否则可能会被扫把型的数据卡掉。

#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#define N 200005
#define LD double
#define eps 1e-9
#define ege 1e-4
#define inf 1000000000000LL
using namespace std;
struct use{int x;LD y;}pri[N];
struct uss{
    int x,y;LD vv;
    bool operator<(const uss&a)const{return y<a.y;}
}qz[N];
int point[N]={0},next[N],en[N],tot=0,ll,uu,siz[N],cur=0,mx,rt,root,mxdep,zh[N],dep[N],
    fa[N],pl,pr,qr,qt;
LD va[N],ans,que[2][N],sum[N],as,hi;
bool vi[N]={false};
int in(){
    char ch=getchar();int x=0;
    while(ch<'0'||ch>'9') ch=getchar();
    while(ch>='0'&&ch<='9'){
        x=x*10+ch-'0';ch=getchar();
    }return x;}
int cmp(LD x,LD y){
    if (y-x>eps) return -1;
    return 0;}
void add(int u,int v,LD vv){
    next[++tot]=point[u];point[u]=tot;en[tot]=v;va[tot]=vv;
    next[++tot]=point[v];point[v]=tot;en[tot]=u;va[tot]=vv;}
void getrt(int u,int ff,int nn){
    int i,v,mz=0;siz[u]=1;
    for (i=point[u];i;i=next[i]){
        if ((v=en[i])==ff||vi[v]) continue;
        getrt(v,u,nn);siz[u]+=siz[v];
        mz=max(mz,siz[v]);
    }mz=max(mz,nn-siz[u]);
    if (mz<mx){mx=mz;rt=u;}
}
void bfs(int u,int ff,LD sm,LD xx){
    int head,tail,i,v;
    qr=min(mxdep,uu);cur^=1;pl=1;pr=0;
    head=tail=0;fa[u]=ff;
    zh[++tail]=u;dep[u]=1;sum[u]=sm;
    for (i=0;i<=qr;++i) que[cur][i]=que[cur^1][i];
    while(head!=tail){
        u=zh[++head];mxdep=max(mxdep,dep[u]);
        if (dep[u]>uu) return;
        que[cur][dep[u]]=max(que[cur][dep[u]],sum[u]);
        while(qr+dep[u]>uu&&qr) --qr;
        while(qr+dep[u]>=ll&&qr){
            while(pl<=pr&&cmp(que[cur^1][qr],pri[pr].y)>=0) --pr;
            pri[++pr]=(use){qr,que[cur^1][qr]};--qr;
        }while(pl<=pr&&pri[pl].x+dep[u]>uu) ++pl;
        if (pl<=pr){
            ans=max(ans,sum[u]+pri[pl].y);
            if (cmp(ans,0.)>=0) return;
        }for (i=point[u];i;i=next[i]){
            if (vi[v=en[i]]||v==fa[u]) continue;
            fa[v]=u;dep[v]=dep[u]+1;
            sum[v]=sum[u]+va[i]-xx;
            zh[++tail]=v;
        }
    }
}
LD calc(int u,LD xx){
    int i,v;mxdep=0;ans=-(LD)inf;
    for (cur=0,i=min(siz[u],uu);i>=0;--i) que[0][i]=que[1][i]=-(LD)inf;
    for (qt=0,i=point[u];i;i=next[i]){
        if (vi[v=en[i]]) continue;
        qz[++qt]=(uss){v,siz[v],va[i]};
    }sort(qz+1,qz+qt+1);
    for (i=1;i<=qt;++i){
        bfs(qz[i].x,u,qz[i].vv-xx,xx);
        if (cmp(ans,0.)>=0) return ans;
    }mxdep=min(mxdep,uu);
    for (i=ll;i<=mxdep;++i) ans=max(ans,que[cur][i]);
    return ans;}
void work(int u){
    int i,v;LD l,r,mid;
    l=as;r=hi;
    while(r-l>ege){
        mid=(l*9.+r)/10.;
        if (cmp(calc(u,mid),0.)>=0) l=mid;
        else r=mid;
    }as=l;vi[u]=true;
    for (i=point[u];i;i=next[i]){
        if (vi[v=en[i]]) continue;
        getrt(v,u,mx=siz[v]);work(rt);
    }
}
int main(){
    int n,i,u,v;LD vv;
    n=in();ll=in();uu=in();
    for (hi=0.,i=1;i<n;++i){
        u=in();v=in();vv=(LD)in();
        add(u,v,vv);hi=max(hi,vv);
    }tot=0;getrt(1,0,mx=n);
    tot=2*n-2;work(rt);
    printf("%.3f\n",as);
}
View Code

 

posted @ 2015-06-26 20:30  Rivendell  阅读(506)  评论(0编辑  收藏  举报