[hash][差分][虚树] Jzoj P6011 天天爱跑步
题解
- 题目大意:给定n个狗和n个狗窝,每只狗只能向(x+y,y)(x-y,x)(x,y+x)(x,y-x)四个方向走,问每个狗都有窝住的最小步数和
- 其实狗往狗窝走和狗窝往狗走的代价其实是一样的,这样的话就可以把狗和狗窝看成等价的点
- 题目上有一句话“狗都不能在坐标轴上或在到达其它象限内的位置”,也就是狗只能在一次象限里,那么反观狗走的四个方向,肯定是有一个方向是走不了的
- 对于剩下的三个方向,有两个是向下走的,有一个是向上走的,把加看成是儿子,把减看成是父亲,这不就形成了一棵二叉树的形状
- 就可以把狗和狗窝全部打进一棵二叉树中,但是这样的话,这棵树显然会大到爆炸,那么我们就可以只用把有用的点和状态记录下来,也就类似与建一棵虚树
- 对于存点的话,可以用hash或map,不过hash快很多
- 那么狗和狗窝的距离,显然就是在树上两点到lca的距离和
- 令狗代表的点的权值为1,狗窝代表的点的权值为-1,将每个点到根的路径都加上该点的权值,类似于树上差分的思想,最终的答案就是所有的点的权值*边的长度之和
- 最后来考虑一下,这个树边的长度该怎么求,其实求步数其实就类似与求gcd的过程,辗转相除法
代码
1 #include <cstdio> 2 #include <algorithm> 3 #define ll long long 4 #define M 4000010 5 #define N 50010 6 #define mo 19260817 7 #define Hash 4000 8 using namespace std; 9 struct edge { int to,from; }e[M]; 10 struct node { ll l,r; }Q[M]; 11 int n,cnt,tot,hash[mo],f[M],P[2*N],p[N],q[N],head[M],L[M],R[M],bz[Hash+10][Hash+10]; 12 ll ans,x,y; 13 bool cmp1(int x,int y) { return Q[x].l<Q[y].l; } 14 bool cmp2(int x,int y) { return Q[x].r<Q[y].r; } 15 int gethash(ll x,ll y) 16 { 17 if (x<=Hash&&y<=Hash) 18 { 19 if (!bz[x][y]) bz[x][y]=++tot,Q[tot]=(node){x,y}; 20 return bz[x][y]; 21 } 22 int p=(x%mo*20+y%mo*11)%mo; 23 while (hash[p]&&(Q[hash[p]].l!=x||Q[hash[p]].r!=y)) p=(p+1)%mo; 24 if (!hash[p]) hash[p]=++tot,Q[tot]=(node){x,y}; 25 return hash[p]; 26 } 27 int insert(ll x,ll y) 28 { 29 int p=gethash(x,y),q=0; 30 if (x&&y) 31 { 32 if (x<y) q=(y%x==0)?insert(x,x):insert(x,y%x); else q=insert(x%y,y); 33 e[++cnt].to=p,e[cnt].from=head[q],head[q]=cnt; 34 } 35 return p; 36 } 37 void calc(ll x,ll y) 38 { 39 if (!x) return; 40 calc(L[x],(Q[L[x]].l-Q[x].l)/Q[x].r); 41 if (Q[x].l) calc(R[x],(Q[R[x]].r-Q[x].r)/Q[x].l); 42 f[x]+=f[L[x]]+f[R[x]],ans+=y*abs(f[x]); 43 } 44 int main() 45 { 46 freopen("a.in","r",stdin),freopen("a.out","w",stdout),scanf("%d",&n); 47 for (int i=1;i<=n;i++) scanf("%lld%lld",&x,&y),p[i]=insert(x,y); 48 for (int i=1;i<=n;i++) scanf("%lld%lld",&x,&y),q[i]=insert(x,y); 49 for (int i=1;i<=tot;i++) 50 { 51 P[P[0]=1]=i; for (int j=head[i];j;j=e[j].from) P[++P[0]]=e[j].to; 52 if (Q[i].l<Q[i].r) sort(P+1,P+P[0]+1,cmp1); else sort(P+1,P+P[0]+1,cmp2); 53 for (int j=2;j<=P[0];j++) if (P[j]^P[j-1]) if (Q[i].l<Q[i].r) L[P[j-1]]=P[j]; else R[P[j-1]]=P[j]; 54 } 55 for (int i=1;i<=n;i++) f[p[i]]++,f[q[i]]--; 56 for (int i=1;i<=tot;i++) if (!Q[i].l) calc(i,0); 57 printf("%lld",ans); 58 }