题目链接:
题目大意:
给出一棵二叉树,每个点具有权值,给出q次查询,每次查询给出一个X,找到到达点v的概率(表示为 7x/2y ),行走规则为,若X>w[u],那么走到左孩子和右孩子的概率分别是1/2,X
题目分析:
- 因为没有强制在线,所以树状数组也是可以做的,大致思路如下:
- 首先我们要对所有出现的数进行离散化,因为包括查询在内数的个数不会超过 105 。
- 然后对数建立两个树状数组,记录数从根到当前点额路径上的数出现的次数(两个分别代表向左走时的次数,和向右走时的次数)。
- 然后进行dfs,在回溯的过程删掉之前的操作,保证树状数组维护的量是当前点到根的这条路径上的。
- 然后对于每个询问,就是在到达这个点时,因为到它的路径已经处理过,所以我们知道了从根到这个点的向右走的总次数,从根到这个点的向左走的总数。
- 那么按照题目中的表示方法,x = right_less(7的指数);
- y = 3* ( right_less , left_less ) + right_larger + left_larger;
- (因为原数的乘法对应到指数上就是加法)
- 那么既然是主席树的练习,当然就算为了练习,也是要来一发主席树的。
- 做法相对于离线做法更加简单,且相似。
- 首先就是对数进行离散化,但是只需要对点权进行离散化
- 然后对每个点建一棵线段树(采用主席树的方式,能够优化空间复杂度到 O(2n⋅logn)
- 查询每个点的时候,每棵线段树维护的数据同上,计算方法也同上。
- 两种方法的时间复杂度相同,空间复杂度第二个略大,但是实现上主席树更加简洁。
AC代码:
树状数组实现:
#pragma comment(linker, "/STACK:1024000000,1024000000")
#include <iostream>
#include <cstring>
#include <cstdio>
#include <algorithm>
#include <vector>
#define MAX 100007
using namespace std;
int t,n,m,q;
int w[MAX];
vector<int> e[MAX];
int ans[MAX][2];
int lef[MAX<<1];
int rig[MAX<<1];
int num[MAX],cnum;
void addEdge ( int u , int v )
{
e[u].push_back ( v );
}
struct Query
{
int id,x,v;
}temp;
vector<Query> a[MAX];
int lowbit ( int x )
{
return x&-x;
}
void Clear ( )
{
for ( int i = 0 ; i < MAX ; i++ )
{
e[i].clear();
a[i].clear();
}
memset ( lef , 0 , sizeof ( lef ) );
memset ( rig , 0 , sizeof ( rig ) );
cnum = 1;
}
void add ( int c[] , int x , int v )
{
while ( x < cnum )
{
c[x] += v;
x += lowbit ( x );
}
}
int sum ( int c[] , int x )
{
int ret = 0;
while ( x )
{
ret += c[x];
x -= lowbit ( x );
}
return ret;
}
int bsearch ( int x )
{
int l=1,r=cnum,mid;
while ( l != r )
{
mid = l+r>>1;
if ( num[mid] == x ) return mid;
if ( num[mid] < x ) l = mid+1;
else r= mid;
}
return mid;
}
void dfs ( int u )
{
for ( int i = 0 ; i < a[u].size(); i++ )
{
int id = a[u][i].id;
int x = bsearch ( a[u][i].x );
if ( sum ( lef , x )- sum ( lef , x-1 ) + sum(rig , x ) - sum ( rig , x-1 ) > 0 )
{
ans[id][0] = -1;
continue;
}
int ll = sum ( lef , x-1 );
int lr = sum ( lef , cnum-1 ) - sum ( lef , x );
int rl = sum ( rig , x-1 );
int rr = sum ( rig , cnum-1 ) - sum ( rig , x );
ans[id][0] = rl;
ans[id][1] = (rl+ll)*3 + rr + lr;
}
int x = bsearch ( w[u] );
if ( e[u].size() == 0 ) return;
int v = e[u][0];
if ( v != -1 )
{
add ( lef , x , 1 );
dfs ( v );
add ( lef , x , -1 );
}
v = e[u][1];
if ( v != -1 )
{
add ( rig , x , 1 );
dfs ( v );
add ( rig , x , -1 );
}
}
int main ( )
{
scanf ( "%d" , &t );
while ( t-- )
{
Clear();
scanf ( "%d" , &n );
for ( int i = 1 ; i <= n ; i++ )
{
scanf ( "%d" , &w[i] );
num[cnum++] = w[i];
}
scanf ( "%d" , &m );
int u,x,y;
for ( int i = 0 ; i < m ; i++ )
{
scanf ( "%d%d%d" , &u , &x , &y );
addEdge ( u , x );
addEdge ( u , y );
}
scanf ( "%d" , &q );
for ( int i = 0 ; i < q ; i++ )
{
scanf ( "%d%d" , &temp.v , &temp.x );
temp.id = i;
a[temp.v].push_back ( temp );
num[cnum++] = temp.x;
}
sort ( num+1 , num+cnum );
cnum = unique ( num+1 , num+cnum )-num;
dfs ( 1 );
for ( int i = 0 ; i < q ; i++ )
{
if ( ans[i][0] == -1 )
puts ( "0" );
else
printf ( "%d %d\n" , ans[i][0] , ans[i][1] );
}
}
return 0;
}
主席树实现:
#pragma comment(linker, "/STACK:1024000000,1024000000")
#include <iostream>
#include <cstring>
#include <cstdio>
#include <algorithm>
#include <vector>
#define MAX 100007
using namespace std;
int t,n,m,q;
int w[MAX];
vector<int> e[MAX];
int ans[MAX][2];
int lef[MAX<<1];
int rig[MAX<<1];
int num[MAX],cnum;
void addEdge ( int u , int v )
{
e[u].push_back ( v );
}
struct Query
{
int id,x,v;
}temp;
vector<Query> a[MAX];
int lowbit ( int x )
{
return x&-x;
}
void Clear ( )
{
for ( int i = 0 ; i < MAX ; i++ )
{
e[i].clear();
a[i].clear();
}
memset ( lef , 0 , sizeof ( lef ) );
memset ( rig , 0 , sizeof ( rig ) );
cnum = 1;
}
void add ( int c[] , int x , int v )
{
while ( x < cnum )
{
c[x] += v;
x += lowbit ( x );
}
}
int sum ( int c[] , int x )
{
int ret = 0;
while ( x )
{
ret += c[x];
x -= lowbit ( x );
}
return ret;
}
int bsearch ( int x )
{
int l=1,r=cnum,mid;
while ( l != r )
{
mid = l+r>>1;
if ( num[mid] == x ) return mid;
if ( num[mid] < x ) l = mid+1;
else r= mid;
}
return mid;
}
void dfs ( int u )
{
for ( int i = 0 ; i < a[u].size(); i++ )
{
int id = a[u][i].id;
int x = bsearch ( a[u][i].x );
if ( sum ( lef , x )- sum ( lef , x-1 ) + sum(rig , x ) - sum ( rig , x-1 ) > 0 )
{
ans[id][0] = -1;
continue;
}
int ll = sum ( lef , x-1 );
int lr = sum ( lef , cnum-1 ) - sum ( lef , x );
int rl = sum ( rig , x-1 );
int rr = sum ( rig , cnum-1 ) - sum ( rig , x );
ans[id][0] = rl;
ans[id][1] = (rl+ll)*3 + rr + lr;
}
int x = bsearch ( w[u] );
if ( e[u].size() == 0 ) return;
int v = e[u][0];
if ( v != -1 )
{
add ( lef , x , 1 );
dfs ( v );
add ( lef , x , -1 );
}
v = e[u][1];
if ( v != -1 )
{
add ( rig , x , 1 );
dfs ( v );
add ( rig , x , -1 );
}
}
int main ( )
{
scanf ( "%d" , &t );
while ( t-- )
{
Clear();
scanf ( "%d" , &n );
for ( int i = 1 ; i <= n ; i++ )
{
scanf ( "%d" , &w[i] );
num[cnum++] = w[i];
}
scanf ( "%d" , &m );
int u,x,y;
for ( int i = 0 ; i < m ; i++ )
{
scanf ( "%d%d%d" , &u , &x , &y );
addEdge ( u , x );
addEdge ( u , y );
}
scanf ( "%d" , &q );
for ( int i = 0 ; i < q ; i++ )
{
scanf ( "%d%d" , &temp.v , &temp.x );
temp.id = i;
a[temp.v].push_back ( temp );
num[cnum++] = temp.x;
}
sort ( num+1 , num+cnum );
cnum = unique ( num+1 , num+cnum )-num;
dfs ( 1 );
for ( int i = 0 ; i < q ; i++ )
{
if ( ans[i][0] == -1 )
puts ( "0" );
else
printf ( "%d %d\n" , ans[i][0] , ans[i][1] );
}
}
return 0;
}