写了四五道点分治的题目了,算是比较理解点分治是什么东西了吧= =
点分治主要用来解决点对之间的问题的,比如距离为不大于K的点有多少对。
这道题要求距离等于K的点对中连接两点的最小边数。
那么其实道理是一样的。先找重心,然后先从重心开始求距离dis和边数num,更新ans,再从重心的儿子开始求得dis和num,减去这部分答案
因为这部分的答案中,从重心开始的两条链有重叠部分,所以要剪掉
基本算是模板题,但是减去儿子的答案的那部分还有双指针那里调了好久,所以还不算特别熟练。。
PS跑了27秒慢到飞起,不过代码短一点,看起来比较清晰
#include<stdio.h> #include<string.h> #include<algorithm> #define INF 0x3f3f3f3f using namespace std; ; struct node{ int to,next,cost; }e[maxn*]; struct data{ int l,e; }dis[maxn]; ]; void insert(int u, int v, int w){ e[++tot].to=v; e[tot].next=head[u]; head[u]=tot; e[tot].cost=w; } void getroot(int u, int f){ ; size[u]=; for (int i=head[u],v; i; i=e[i].next){ if (vis[v=e[i].to] || v==f) continue; getroot(v,u); size[u]+=size[v]; mx=max(mx,size[v]); } mx=max(mx,total-size[u]); if (mx<sz) sz=mx,root=u; } void getdis(int u, int f, int len, int num){ dis[++p].l=len; dis[p].e=num; for (int i=head[u],v; i; i=e[i].next){ if (vis[v=e[i].to] || v==f) continue; getdis(v,u,len+e[i].cost,num+); } } bool cmp(data a, data b){ if (a.l==b.l) return a.e<b.e; return a.l<b.l; ///// } void count(int u, int len, int num, int f){ p=; getdis(u,,len,num); //这里要将len和num传下去。。 ,r=p; sort(dis+,dis++p,cmp); while (l<=r){ //注意这里要用<=,WA了几发 while (l<r && dis[l].l+dis[r].l>K) r--; for (int k=r; dis[l].l+dis[k].l==K; k--) ans[dis[l].e+dis[k].e]+=f; l++; } } void work(int u){ total=size[u]?size[u]:n; sz=INF; getroot(u,); u=root; vis[u]=; count(u,,,); for (int i=head[u],v; i; i=e[i].next){ if (vis[v=e[i].to]) continue; count(v,e[i].cost,,-); work(v); } } int main(){ scanf("%d%d", &n, &K); ,u,v,w; i<n; i++) scanf("%d%d%d", &u, &v, &w),u++,v++,insert(u,v,w),insert(v,u,w); work(); ; i<n; i++) ;} puts("-1"); ; }