HDU4605 Magic Ball Game(可持久化线段树)
当魔术球比赛出现时,基米立即掉入其中。有趣的游戏由N个球组成,每个球的权重均为w [i]。这N个球以第一个球为根形成一棵有根的树。游戏中的任何球都有0或2个子球。如果一个节点有2个子球,我们可以定义一个为左子球,另一个为右子球。
规则很简单:当Kimi决定放下重量为X的魔术球时,该球将从树根向下穿过树。当魔术球到达树上的某个节点时,就有可能被抓住并停止滚动,或者继续向左或向右滚动。当球停止时游戏结束,并且游戏的最终得分取决于停止的节点。
经过长时间的游戏,Kimi现在可以找到游戏的关键。当魔术球到达节点u的权重w [u]时,它遵循以下定律:
1如果X = w [u]或节点u没有子球,则魔球停止。
2如果X <w [u],则魔术球向左或向右滚动的可能性为1/2。
3如果X> w [u],则魔球将以1/8的可能性向下滚动到其左子对象,而右滚的可能性为7/8。
为了选择正确的魔术球并实现目标,Kimi想知道重量为X的魔术球越过节点v的可能性。无论魔术球如何滚动,它都会计算节点v是否存在于节点上。魔术球走的路径。
手动计算很有趣,但是程序员有自己的方法来找到答案。现在考虑到游戏中的树和所有Kimi的查询,您需要回答他想知道的可能性。
输入值
输入包含几个测试用例。输入的第一行中将存在整数T(T≤15),指示测试用例的数量。
每个测试用例均以整数N(1≤N≤105)开头,表示树中的节点数。下一行包含N个整数w [i],表示树中每个节点的权重。 (1≤i≤N,1≤w [i]≤109,N为奇数)
下一行包含关系M的数量。接下来的M行分别具有三个整数u,a和b(1≤u,a,b≤N),表示节点a和b分别是左子节点和右子节点节点u的。您可以假设树正好包含N个节点和(N-1)个边。
下一行给出查询数量Q(1≤Q≤105)。以下Q行(每个行都有两个整数v和X(1≤v≤N,1≤X≤109))描述了所有查询。
输出量
如果魔术球不可能到达节点v,则输出单个0。否则,您可能会很容易发现答案将采用7x / 2y的格式。您只需要为每个查询输出x和y,用空格分隔。每个答案都应该放在一行中。
Solution
建立两颗主席树分别统计从根节点到当前节点的路径中,下一步是左孩子的节点情况和下一步是右孩子的节点情况,好题
//如果x=w[u],则魔球停止 //如果x>w[u],则魔球1/8向左,7/8向右 //如果x<w[u],则向左向右的可能性均为1/2 //节点v和根节点的路径上,如果有一个w[u]==x,则输出0 //否则分别计算 //小于x的节点中,如果路径的下一步是左孩子,则y+=3 //如果路径的下一步是右孩子,则x+=1,y+=3 //大于x的节点总数加起来是tt,y+=tt #include<bits/stdc++.h> using namespace std; const int maxn=1e5+100; const int M=maxn*40; int n,m,q; int t[maxn],a[maxn]; int tot; int T[maxn]; int lson[M],rson[M],c[M][2]; //c[i][0]表示起点到i的路径中,下一步是左孩子的情况 //c[i][1]表示起点到i的路径中,下一步是右孩子的情况 int build (int l,int r) { int root=tot++; c[root][0]=c[root][1]=0; if (l!=r) { int mid=(l+r)>>1; lson[root]=build(l,mid); rson[root]=build(mid+1,r); } return root; } int up (int root,int p,int v,int f) { int newRoot=tot++; int tmp=newRoot; int l=1,r=m; c[newRoot][f]=c[root][f]+v; c[newRoot][!f]=c[root][!f]; while (l<r) { int mid=(l+r)>>1; if (p<=mid) { lson[newRoot]=tot++; rson[newRoot]=rson[root]; newRoot=lson[newRoot]; root=lson[root]; r=mid; } else { rson[newRoot]=tot++; lson[newRoot]=lson[root]; newRoot=rson[newRoot]; root=rson[root]; l=mid+1; } c[newRoot][f]=c[root][f]+v; c[newRoot][!f]=c[root][!f]; } return tmp; } int query (int left_root,int right_root,int l,int r,int L,int R,int f) { if (L>R) return 0; if (l>=L&&r<=R) return c[right_root][f]-c[left_root][f]; int mid=(l+r)>>1; int ans=0; if (L<=mid) ans+=query(lson[left_root],lson[right_root],l,mid,L,R,f); if (R>mid) ans+=query(rson[left_root],rson[right_root],mid+1,r,L,R,f); return ans; } vector<int> g[maxn]; void dfs (int u) { for (int i=0;i<g[u].size();i++) { T[g[u][i]]=up(T[u],a[u],1,i); dfs(g[u][i]); } } struct qnode { int x,y; }Q[maxn]; int main () { int _; scanf("%d",&_); while (_--) { scanf("%d",&n); tot=0; int cnt=0; for (int i=1;i<=n;i++) scanf("%d",a+i),t[++cnt]=a[i],g[i].clear(); int mm; scanf("%d",&mm); while (mm--) { int u,a,b; scanf("%d%d%d",&u,&a,&b); g[u].push_back(a); g[u].push_back(b); } scanf("%d",&q); for (int i=1;i<=q;i++) { scanf("%d%d",&Q[i].x,&Q[i].y); t[++cnt]=Q[i].y; } sort(t+1,t+cnt+1); m=unique(t+1,t+cnt+1)-t-1; for (int i=1;i<=n;i++) a[i]=upper_bound(t+1,t+m+1,a[i])-t-1; for (int i=1;i<=q;i++) Q[i].y=upper_bound(t+1,t+m+1,Q[i].y)-t-1; T[1]=build(1,m); dfs(1); for (int i=1;i<=q;i++) { if (query(T[1],T[Q[i].x],1,m,Q[i].y,Q[i].y,0)||query(T[1],T[Q[i].x],1,m,Q[i].y,Q[i].y,1)) { printf("0\n"); continue; } int A=query(T[1],T[Q[i].x],1,m,1,Q[i].y-1,0);//向左走的比Q[i].y小的数 int B=query(T[1],T[Q[i].x],1,m,1,Q[i].y-1,1);//向右走的比Q[i].y小的数 int C=query(T[1],T[Q[i].x],1,m,Q[i].y+1,m,0);//向左走的比Q[i].y大的数 int D=query(T[1],T[Q[i].x],1,m,Q[i].y+1,m,1);//向右走的比Q[i].y大的数 printf("%d %d\n",B,C+D+3*(A+B)); } } }