hdu 4605 Magic Ball Game(主席树学习第二弹)

题目链接:

hdu 4605


题目大意:

给出一棵二叉树,每个点具有权值,给出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(2nlogn)
    • 查询每个点的时候,每棵线段树维护的数据同上,计算方法也同上。
  • 两种方法的时间复杂度相同,空间复杂度第二个略大,但是实现上主席树更加简洁。

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;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值