【BZOJ】2152 聪聪可可 点分治

来源:互联网 发布:必读书 知乎 编辑:程序博客网 时间:2024/05/17 23:29

题目传送门

这道题的正解——点分治还是挺好想到的,因为题目已经明确给出这道题是以树为基础的。

这题的统计答案与点分治的模板有些不同,不过也还是挺好想到的。

题目要求任意两点的距离是3的倍数的点对数量,我们可以把这个转化为在有根树中,LCA到其中一点的距离为3的倍数余1,到另一节点的距离为3的倍数余2的点对数量加上LCA到两个节点的距离都为3的倍数的点对数量之和。

就此,我们已经解决了这道题,剩下的就是点分治的模板了。

附上AC代码:

#include <cstdio>#include <cctype>#include <algorithm>#define N 20005using namespace std;struct side{int from,to,w,nt;}s[N<<1];int n,x,y,w,num,h[N],sum,mx[N],size[N],rt,ans,t[3],d[N];bool b[N];inline char nc(){static char ch[100010],*p1=ch,*p2=ch;return p1==p2&&(p2=(p1=ch)+fread(ch,1,100010,stdin),p1==p2)?EOF:*p1++;}inline void read(int &a){static char c=nc();int f=1;for (;!isdigit(c);c=nc()) if (c=='-') f=-1;for (a=0;isdigit(c);a=a*10+c-'0',c=nc());a*=f;return;}inline void add(int x,int y,int w){s[++num]=(side){x,y,w,h[x]},h[x]=num;s[++num]=(side){y,x,w,h[y]},h[y]=num;}inline void so(int x,int fa){size[x]=1,mx[x]=0;for (int i=h[x]; i; i=s[i].nt)if (!b[s[i].to]&&s[i].to!=fa)so(s[i].to,x),size[x]+=size[s[i].to],mx[x]=max(mx[x],size[s[i].to]);mx[x]=max(mx[x],sum-size[x]);if (mx[x]<mx[rt]) rt=x;return;}inline void pre(int x,int fa){++t[d[x]];for (int i=h[x]; i; i=s[i].nt)if (!b[s[i].to]&&s[i].to!=fa)d[s[i].to]=(d[x]+s[i].w)%3,pre(s[i].to,x);return;}inline int calc(int x,int v){t[0]=t[1]=t[2]=0,d[x]=v,pre(x,0);return t[1]*t[2]*2+t[0]*t[0];}inline void work(int x){ans+=calc(x,0),b[x]=1;for (int i=h[x]; i; i=s[i].nt)if (!b[s[i].to]){ans-=calc(s[i].to,s[i].w),sum=size[s[i].to],rt=0;so(s[i].to,0),work(rt);}return;}inline int gcd(int a,int b){return !b?a:gcd(b,a%b);}int main(void){read(n);for (int i=1; i<n; ++i) read(x),read(y),read(w),add(x,y,w%3);sum=mx[0]=n,so(1,0),work(rt);int t=gcd(ans,n*n);printf("%d/%d",ans/t,n*n/t);return 0;}

原创粉丝点击