树上点分治学习记录

跟着机房的潮流学了点分治,发现这个东西其实还蛮好写的,学会思想,很容易YY出来。直接上习题。
POJ 1741 TREE
点分治的模板题,首先设点 x 到当前子树跟root的距离为 gx ,则满足 gx+gyk 可以加进答案,但是注意如果 x,y 在同一棵子树中,就要删去对答案的贡献,以为 x,y 会在其所在的子树中在计算一次。注意无根树转有根树的过程,需要选取树的重心防止复杂度从 O(nlog2n) 退化为 O(n2) code:

#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#define inf 100000
using namespace std;
struct hp{
    int u,v,w;
}a[20001];
struct hq{
    int size;
}tree[10001];
bool done[10001];
int point[10001],next[20001];
int len[10001];
int n,m,e=0,t=0;
int size,root,sizenow,tot,ans=0;
void add(int u,int v,int w)
{
    e++; a[e].u=u; a[e].v=v; a[e].w=w; next[e]=point[u]; point[u]=e;
    e++; a[e].u=v; a[e].v=u; a[e].w=w; next[e]=point[v]; point[v]=e;
}
void findsize(int now,int last)
{
    int i,tmp=0;
    sizenow++;
    for (i=point[now];i;i=next[i])
      if (a[i].v!=last&&!done[a[i].v])
        findsize(a[i].v,now);
}
void findroot(int now,int last)
{
    int i,tmp=0;
    tree[now].size=1; tmp=0;
    for (i=point[now];i;i=next[i])
      if (a[i].v!=last&&!done[a[i].v])
        {
          findroot(a[i].v,now);
          tree[now].size+=tree[a[i].v].size;
          if (tree[a[i].v].size>tmp)
            tmp=tree[a[i].v].size;
        }
    if (sizenow-tree[now].size>tmp) tmp=sizenow-tree[now].size;
    if (tmp<size)
      {root=now; size=tmp;}
}
void findroad(int now,int last,int road)
{
    int i,tmp=0;
    len[++t]=road; 
    if (road>m) return;
    for (i=point[now];i;i=next[i])
      if (a[i].v!=last&&!done[a[i].v])
        findroad(a[i].v,now,road+a[i].w);
}
int calc(int k)
{
    int i,l,r,ret=0;
    sort(len+1,len+t+1);
    for (l=1,r=t;l<r;)
      {
        if (len[l]+len[r]<=k) ret+=r-l++;
        else r--;
      }
    return ret;
}
void work(int now)
{
    int i;
    sizenow=0; t=0;  size=inf;
    findsize(now,0);
    findroot(now,0);
    findroad(root,0,0);
    done[root]=true;
    ans+=calc(m);
    for (i=point[root];i;i=next[i])
      if (!done[a[i].v])
        {
          t=0; sizenow=0; size=inf;
          findroad(a[i].v,0,0);
          ans-=calc(m-2*a[i].w);
          findsize(a[i].v,0);
          findroot(a[i].v,0);
          work(a[i].v);    
        }
}
int main()
{
    int i,x,y,z;
    scanf("%d%d",&n,&m);
    while (n!=0&&m!=0)
      {
        e=0; ans=0;
        memset(point,0,sizeof(point));
        memset(done,false,sizeof(done));
        for (i=1;i<=n-1;++i)
         {
           scanf("%d%d%d",&x,&y,&z);
           add(x,y,z);
         }
        work(1);
        printf("%d\n",ans);
        scanf("%d%d",&n,&m);
      }
}

FJOI 2014 BZOJ 4016 最短路径树问题
spfa跑出最短路图,然后按字典序DFS,注意,路径长度相同同时也要求点数也为K,然后乱搞一下就好了。
code:

#include<iostream> 
#include<cstdio> 
#include<cstring> 
#include<algorithm> 
#include<cmath>
#include<vector>
#define inf 100001 
using namespace std; 
int size[30001]; 
struct hp{ 
    int u,v,w; 
    bool f; 
}ai[120001],ax[120001];
vector<hp>a[30001]; 
int queue[1000001]; 
bool exist[30001],done[30001]; 
int point[30001],next[120001];
int dis[30001]; 
int pointi[30001],nexti[120001]; 
int e=0,n,m,len=0,sizenow,sizet,root,t,ans=0,dot; 
int L[30001][2]; 
hp makeedge(int u,int v,int w) 
{
    struct hp ab;
    ab.u=u; ab.v=v; ab.w=w; ab.f=false;
    return ab;
} 
void add(int u,int v,int w) 
{ 
    e++; ax[e].u=u; ax[e].v=v; ax[e].w=w; ax[e].f=false; next[e]=point[u]; point[u]=e; 
    e++; ax[e].u=v; ax[e].v=u; ax[e].w=w; ax[e].f=false; next[e]=point[v]; point[v]=e; 
} 
void addi(int u,int v,int w) 
{ 
    e++; ai[e].u=u; ai[e].v=v; ai[e].w=w; nexti[e]=pointi[u]; pointi[u]=e; 
    e++; ai[e].u=v; ai[e].v=u; ai[e].w=w; nexti[e]=pointi[v]; pointi[v]=e; 
} 
void spfa() 
{ 
    int head,tail,u,i; 
    memset(dis,127/3,sizeof(dis)); 
    head=0; tail=1; queue[tail]=1; exist[1]=true; dis[1]=0;  
    while (head!=tail) 
      { 
        head=head%1000000+1; 
        u=queue[head]; exist[u]=false; 
        for (i=point[u];i;i=next[i]) 
          if (dis[ax[i].v]>dis[u]+ax[i].w) 
            { 
              dis[ax[i].v]=dis[u]+ax[i].w; 
              if (!exist[ax[i].v]) 
                { 
                  exist[ax[i].v]=true; 
                  tail=tail%1000000+1; 
                  queue[tail]=ax[i].v; 
                } 
            } 
      } 
} 
void findsize(int now,int last) 
{ 
    int i; 
    sizenow++; 
    for (i=pointi[now];i;i=nexti[i]) 
      if (ai[i].v!=last&&!done[ai[i].v]) 
        findsize(ai[i].v,now); 
} 
void findroot(int now,int last) 
{ 
    int i,temp=0; 
    size[now]=1; 
    for (i=pointi[now];i;i=nexti[i]) 
      if (ai[i].v!=last&&!done[ai[i].v]) 
        { 
          findroot(ai[i].v,now); 
          size[now]+=size[ai[i].v]; 
          if (size[ai[i].v]>temp) 
            temp=size[ai[i].v]; 
        } 
    if  (sizenow-size[now]>temp) temp=sizenow-size[now]; 
    if (sizet>temp) 
      { 
        sizet=temp; 
        root=now;   
      } 
} 
void addlen(int now,int last,int dep,int lent) 
{ 
    int i; 
    if (dep>dot) return; 
    if (lent==L[dep][0]) 
      L[dep][1]++; 
    if (lent>L[dep][0]) 
      L[dep][0]=lent,L[dep][1]=1; 
    for (i=pointi[now];i;i=nexti[i]) 
      if (!done[ai[i].v]&&ai[i].v!=last) 
        addlen(ai[i].v,now,dep+1,lent+ai[i].w); 
} 
void makelen(int now,int last,int dep,int lent) 
{ 
    int i;
    if (dep>dot) return; 
    if (len==L[dot-dep+1][0]+lent)  
      ans+=L[dot-dep+1][1]; 
    if (len<L[dot-dep+1][0]+lent) 
      len=L[dot-dep+1][0]+lent,ans=L[dot-dep+1][1]; 
    for (i=pointi[now];i;i=nexti[i]) 
      if (!done[ai[i].v]&&ai[i].v!=last) 
       makelen(ai[i].v,now,dep+1,lent+ai[i].w); 
} 
void findmax(int now) 
{ 
    int i; 
    sizenow=0; sizet=inf; root=0; 
    findsize(now,0); 
    findroot(now,0); 
    memset(L,0,sizeof(L)); 
    L[1][0]=0; L[1][1]=1; done[root]=true;  
    for (i=pointi[root];i;i=nexti[i]) 
      if (!done[ai[i].v]) 
        { 
          makelen(ai[i].v,root,2,ai[i].w); 
          addlen(ai[i].v,root,2,ai[i].w); 
        }
    for (i=pointi[root];i;i=nexti[i]) 
      if (!done[ai[i].v]) 
        findmax(ai[i].v); 
} 
int cmp(const hp &x,const hp &y) 
{ 
    if (x.v<y.v) return 1; 
    else return 0; 
} 
void dfs(int now,int last,int w) 
{ 
    int i,t=0; 
    if (exist[now]) return; 
    if (exist[now]==false&&now!=1) addi(last,now,w); 
    exist[now]=true; 
    sort(a[now].begin(),a[now].end(),cmp); 
    for (i=0;i<a[now].size();++i) 
      if (a[now][i].f&&!exist[a[now][i].v])
        dfs(a[now][i].v,now,a[now][i].w); 
} 
int main() 
{ 
    int i,x,y,z,j;  
    scanf("%d%d%d",&n,&m,&dot); 
    e=0; 
    for (i=1;i<=m;++i) 
      { 
        scanf("%d%d%d",&x,&y,&z); 
        a[x].push_back(makeedge(x,y,z));
        a[y].push_back(makeedge(y,x,z));
        add(x,y,z);
      } 
    spfa(); 
    for (i=1;i<=n;++i)
      for (j=0;j<a[i].size();++j)
        if (a[i][j].w==dis[a[i][j].v]-dis[a[i][j].u])
          a[i][j].f=true;
    memset(exist,false,sizeof(exist));
    e=0; dfs(1,0,0); 
    ans=0; len=0; 
    findmax(1);  
    if (ans==0)
     printf("0 0\n");
    else
      printf("%d %d\n",len,ans); 
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值