跟着机房的潮流学了点分治,发现这个东西其实还蛮好写的,学会思想,很容易YY出来。直接上习题。
POJ 1741 TREE
点分治的模板题,首先设点 x 到当前子树跟 root 的距离为 gx ,则满足 gx+gy≤k 可以加进答案,但是注意如果 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);
}