HDU-5877 Weak Pair(dfs序+归并树/dfs序+离线归并+树状数组/dfs栈+树状数组/dfs作差+树状数组)

博客围绕一棵有n个节点的树,求解满足u是v的祖先且a[u]*a[v]≤k的点对(u,v)数量。介绍了四种解法,包括dfs序+归并树、dfs序+离线归并、dfs栈+树状数组、dfs作差+树状数组,还给出了各解法思路及对应代码。

题意

在一棵有 nn 个节点的树上,求出有多少个点对 (u,v) ,满足 uuv 的祖先且 a[u]a[v]ka[u]∗a[v]≤k
1n1051≤n≤105
0ai1090≤ai≤109
0k10180≤k≤1018

思路

解法一:dfs序+归并树

如果把式子做一些变化,改为 a[v]ka[u]a[v]≤ka[u] ,再用 dfsdfs 序处理出树节点对应的区间,就是比较裸的归并树了,每次查询以某个节点 uu 为祖先的子树中满足条件的 v 的个数。

解法二:dfs序+离线归并

查询一个区间内小于等于某个数的数的个数,转化成离线归并+树状数组会比归并树常数小很多。离线归并并不是归并排序,而是对一个有序的询问队列 QQ ,一个有序的更新队列 U ,通过类似归并的方式回答 QQ ,并将 U 的更新记录在数据结构中。像这道题,只用维护一个 0101 的树状数组,00 表示当前位置上没有数,1 表示有数。对于一个节点 uu ,它贡献出的询问是 L[u]+1,R[u] 中小于等于 ka[u]ka[u] 的数的个数,贡献出的更新是当询问值首次小于等于 a[u]a[u] 时,将树状数组上的 L[u]L[u] 赋为 11 。那只用将询问和更新的队列按键值从小到大排序,然后当更新队头小于等于询问队头时,执行更新即可,下面是一个比较一般的板子。

int u=1;
FOR(i,1,n)
{
    while(u<=n&&U[u].val...Q[i].val)
        BIT.update(U[u].x,...),u++;  //执行更新
    FOR(j,0,(int)Q[i].size()-1)Out[Q[i][j].id]BIT.query(Q[i][j].L,Q[i][j].R,...);  //回答询问
}
解法三:dfs栈+树状数组

如果我们将原式再变一下,当 uvv 的祖先时,求a[u]ka[v]uu 的个数,题目就变成了求合法祖先个数,那只用在遍历到 v 节点时,将 vv 节点的值在树状数组中更新,再直接在树状数组中查询并更新答案,当回溯上来时,再将 v 节点信息清除即可,由于至过大,应在离散的基础上更新查询。

解法四:dfs作差+树状数组

这种方法比上一种方法拓展性更强。当 uuv 的祖先时,我们要求的是合法的 vv 的个数。不难发现,刚遍历到 u 和从 uu 回溯时,两者信息的差值恰好就是 u 这棵子树的信息。那我们只需在刚遍历到 uu 时,求出所有满足条件的点的个数,即将从 u 回溯时,再算一次满足条件点的个数并减去前者,就是 uu 的子树中满足条件点的个数。不难看出,当所求的答案与子树有关且符合作差性质的,dfs 作差是很适合的方法。

代码

解法一:dfs序+归并树
#include<iostream>
#include<cmath>
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<algorithm>
#include<vector>
#define FOR(i,x,y) for(int i=(x);i<=(y);i++)
#define DOR(i,x,y) for(int i=(x);i>=(y);i--)
#define lowbit(x) ((x)&-(x))
#define N 100003
typedef long long LL;
using namespace std;
template<const int maxn,const int maxm>struct Linked_list
{
    int head[maxn],to[maxm],nxt[maxm],tot;
    void clear(){memset(head,-1,sizeof(head));tot=0;}
    void add(int u,int v){to[++tot]=v,nxt[tot]=head[u],head[u]=tot;}
    #define EOR(i,G,u) for(int i=G.head[u];~i;i=G.nxt[i])
};
struct node
{
    int L,R;
    vector<LL>vec;
    void merging(node &x,node &y)
    {
        vec.clear();
        int i=0,j=0;
        while(i<x.vec.size()||j<y.vec.size())
        {
            if(i>=x.vec.size()||j<y.vec.size()&&y.vec[j]<x.vec[i])
                vec.push_back(y.vec[j++]);
            else vec.push_back(x.vec[i++]);
        }
    }
    int check(LL chk){return upper_bound(vec.begin(),vec.end(),chk)-vec.begin();}
};
struct SegmentTree
{
    node nd[N<<2];
    void build(int k,int L,int R,LL *arr)
    {
        nd[k].L=L,nd[k].R=R;
        if(L==R)
        {
            nd[k].vec.clear();
            nd[k].vec.push_back(arr[L]);
            return;
        }
        build(k<<1,L,L+R>>1,arr);
        build(k<<1|1,(L+R>>1)+1,R,arr);
        nd[k].merging(nd[k<<1],nd[k<<1|1]);
    }
    int query(int k,int L,int R,LL chk)
    {
        if(L<=nd[k].L&&nd[k].R<=R)return nd[k].check(chk);
        int mid=nd[k].L+nd[k].R>>1;
        if(R<=mid)return query(k<<1,L,R,chk);
        else if(L>mid)return query(k<<1|1,L,R,chk);
        else return query(k<<1,L,R,chk)+query(k<<1|1,L,R,chk);
    }
};
struct Tree
{
    Linked_list<N,N>G;
    SegmentTree ST;
    int L[N],R[N],ord;
    void reset(){G.clear();ord=0;}
    void build(int n,LL *arr){ST.build(1,1,n,arr);};
    void add(int u,int v){G.add(u,v);}
    void dfs(int u,int fa)
    {
        L[u]=++ord;
        EOR(i,G,u)
        {
            int v=G.to[i];
            if(v!=fa)dfs(v,u);
        }
        R[u]=ord;
    }
    int query(int k,LL chk){return L[k]==R[k]?0:ST.query(1,L[k]+1,R[k],chk);}
}T;
LL a[N],b[N];
bool root[N];

int main()
{
    int Task,n;
    LL k,ans;
    scanf("%d",&Task);
    while(Task--)
    {
        ans=0;
        scanf("%d%lld",&n,&k);
        FOR(i,1,n)root[i]=1;
        FOR(i,1,n)scanf("%lld",&a[i]);
        T.reset();
        FOR(i,1,n-1)
        {
            int u,v;
            scanf("%d%d",&u,&v);
            T.add(u,v);
            root[v]=0;
        }
        FOR(i,1,n)if(root[i])T.dfs(i,-1);
        FOR(i,1,n)b[T.L[i]]=a[i];
        T.build(n,b);
        FOR(i,1,n)
        {
            int ans_=ans;
            if(a[i]==0)ans+=T.R[i]-T.L[i];
            else ans+=T.query(i,k/a[i]);
        }
        printf("%lld\n",ans);
    }
    return 0;
}
解法二:dfs序+离线归并
#include<iostream>
#include<cmath>
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<algorithm>
#define FOR(i,x,y) for(int i=(x);i<=(y);i++)
#define DOR(i,x,y) for(int i=(x);i>=(y);i--)
#define lowbit(x) ((x)&-(x))
#define N 100003
typedef long long LL;
using namespace std;
int a[N],b[N],L[N],R[N],n,ord;
bool root[N];
LL ans,K;
template<const int maxn,const int maxm>struct Linked_list
{
    int head[maxn],to[maxm],nxt[maxm],tot;
    void clear(){memset(head,-1,sizeof(head));tot=0;}
    void add(int u,int v){to[++tot]=v,nxt[tot]=head[u],head[u]=tot;}
    #define EOR(i,G,u) for(int i=G.head[u];~i;i=G.nxt[i])
};
Linked_list<N,N>G;
struct QList
{
    int L,R;
    LL val;
    bool operator <(const QList &_)const
    {
        return val<_.val;
    }
}Q[N];
struct Ulist
{
    int x;
    LL val;
    bool operator <(const Ulist &_)const
    {
        return val<_.val;
    }
}U[N];
struct BinaryIndexedTree
{
    int c[N],n;
    void reset(int _){memset(c,0,sizeof(c));n=_;}
    void update(int k,int val)
    {
        while(k<=n)
        {
            c[k]+=val;
            k+=lowbit(k);
        }
    }
    int _query(int k)
    {
        int res=0;
        while(k>0)
        {
            res+=c[k];
            k^=lowbit(k);
        }
        return res;
    }
    int query(int L,int R){return _query(R)-_query(L-1);}
}BIT;
void dfs(int u,int fa)
{
    L[u]=++ord;
    EOR(i,G,u)
    {
        int v=G.to[i];
        if(v==fa)continue;
        dfs(v,u);
    }
    R[u]=ord;
}

void solve()
{
    int u=1;
    FOR(i,1,n)
    {
        while(u<=n&&U[u].val<=Q[i].val)
            BIT.update(U[u++].x,1);
        ans+=BIT.query(Q[i].L,Q[i].R);
    }
}

int main()
{
    int T;
    scanf("%d",&T);
    while(T--)
    {
        ans=0;
        scanf("%d%lld",&n,&K);
        FOR(i,1,n)root[i]=1;
        FOR(i,1,n)
        {
            int t;
            scanf("%d",&t);
            Q[i].val=(t?K/t:5e18);
            U[i].val=t;
        }
        G.clear();
        BIT.reset(n);
        ord=0;
        FOR(i,1,n-1)
        {
            int u,v;
            scanf("%d%d",&u,&v);
            G.add(u,v);
            root[v]=0;  
        }
        FOR(i,1,n)if(root[i])dfs(i,-1);
        FOR(i,1,n)
        {
            Q[i].L=L[i]+1,Q[i].R=R[i];
            U[i].x=L[i];
        }
        sort(Q+1,Q+1+n);
        sort(U+1,U+1+n);
        solve();
        printf("%lld\n",ans);
    }
    return 0;
}
解法三:dfs栈+树状数组
#include<iostream>
#include<cmath>
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<algorithm>
#include<vector>
#define FOR(i,x,y) for(int i=(x);i<=(y);i++)
#define DOR(i,x,y) for(int i=(x);i>=(y);i--)
#define lowbit(x) ((x)&-(x))
#define N 100003
typedef long long LL;
using namespace std;
int a[N],b[N],n;
LL ans,K;
int lbnd(LL k){return lower_bound(b+1,b+1+n,k)-b;}
int ubnd(LL k){return upper_bound(b+1,b+1+n,k)-b;}
template<const int maxn,const int maxm>struct Linked_list
{
    int head[maxn],to[maxm],nxt[maxm],tot;
    void clear(){memset(head,-1,sizeof(head));tot=0;}
    void add(int u,int v){to[++tot]=v,nxt[tot]=head[u],head[u]=tot;}
    #define EOR(i,G,u) for(int i=G.head[u];~i;i=G.nxt[i])
};
struct BinaryIndexedTree
{
    int c[N],n;
    void reset(int _){n=_;memset(c,0,sizeof(c));}
    void update(int k,int val)
    { 
        while(k<=n)
        {
            c[k]+=val;
            k+=lowbit(k);
        }
    }
    int query(int k)
    {
        int res=0;
        k=min(k,n);
        while(k)
        {
            res+=c[k];
            k^=lowbit(k);
        }
        return res;
    }
};
struct Tree
{
    Linked_list<N,N>G;
    BinaryIndexedTree BIT;
    int p[N],n;
    void reset(int _,int *arr)
    {
        BIT.reset(n=_);G.clear();
        FOR(i,1,n)p[i]=arr[i];
    }
    void add(int u,int v){G.add(u,v);}
    void dfs(int u,int fa)
    {
        if(!p[u])ans+=BIT.query(n);
        else ans+=BIT.query(ubnd(K/p[u])-1);
        BIT.update(lbnd(p[u]),1);
        EOR(i,G,u)
        {
            int v=G.to[i];
            if(v!=fa)dfs(v,u);
        }
        BIT.update(lbnd(p[u]),-1);
    }
}T;
bool root[N];

int main()
{
    int Task;
    scanf("%d",&Task);
    while(Task--)
    {
        ans=0;
        scanf("%d%lld",&n,&K);
        FOR(i,1,n)root[i]=1;
        FOR(i,1,n)scanf("%lld",&a[i]),b[i]=a[i];
        sort(b+1,b+1+n);
        T.reset(n,a);
        FOR(i,1,n-1)
        {
            int u,v;
            scanf("%d%d",&u,&v);
            T.add(u,v);
            root[v]=0;
        }
        FOR(i,1,n)if(root[i])T.dfs(i,-1);
        printf("%lld\n",ans);
    }
    return 0;
}
解法四:dfs作差+树状数组
#include<iostream>
#include<cmath>
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<algorithm>
#include<vector>
#define FOR(i,x,y) for(int i=(x);i<=(y);i++)
#define DOR(i,x,y) for(int i=(x);i>=(y);i--)
#define lowbit(x) ((x)&-(x))
#define N 100003
typedef long long LL;
using namespace std;
int a[N],b[N],n;
LL ans,K;
int lbnd(LL k){return lower_bound(b+1,b+1+n,k)-b;}
int ubnd(LL k){return upper_bound(b+1,b+1+n,k)-b;}
template<const int maxn,const int maxm>struct Linked_list
{
    int head[maxn],to[maxm],nxt[maxm],tot;
    void clear(){memset(head,-1,sizeof(head));tot=0;}
    void add(int u,int v){to[++tot]=v,nxt[tot]=head[u],head[u]=tot;}
    #define EOR(i,G,u) for(int i=G.head[u];~i;i=G.nxt[i])
};
struct BinaryIndexedTree
{
    int c[N],n;
    void reset(int _){n=_;memset(c,0,sizeof(c));}
    void update(int k,int val)
    { 
        while(k<=n)
        {
            c[k]+=val;
            k+=lowbit(k);
        }
    }
    int query(int k)
    {
        int res=0;
        k=min(k,n);
        while(k)
        {
            res+=c[k];
            k^=lowbit(k);
        }
        return res;
    }
};
struct Tree
{
    Linked_list<N,N>G;
    BinaryIndexedTree BIT;
    int p[N],n;
    void reset(int _,int *arr)
    {
        BIT.reset(n=_);G.clear();
        FOR(i,1,n)p[i]=arr[i];
    }
    void add(int u,int v){G.add(u,v);}
    void dfs(int u,int fa)
    {
        int cnt=0;
        if(!p[u])cnt=BIT.query(n);
        else cnt=BIT.query(ubnd(K/p[u])-1);
        EOR(i,G,u)
        {
            int v=G.to[i];
            if(v!=fa)dfs(v,u);
        }
        if(!p[u])ans+=BIT.query(n)-cnt;
        else ans+=BIT.query(ubnd(K/p[u])-1)-cnt;
        BIT.update(lbnd(p[u]),1);
    }
}T;
bool root[N];

int main()
{
    int Task;
    scanf("%d",&Task);
    while(Task--)
    {
        ans=0;
        scanf("%d%lld",&n,&K);
        FOR(i,1,n)root[i]=1;
        FOR(i,1,n)scanf("%lld",&a[i]),b[i]=a[i];
        sort(b+1,b+1+n);
        T.reset(n,a);
        FOR(i,1,n-1)
        {
            int u,v;
            scanf("%d%d",&u,&v);
            T.add(u,v);
            root[v]=0;
        }
        FOR(i,1,n)if(root[i])T.dfs(i,-1);
        printf("%lld\n",ans);
    }
    return 0;
}
内容概要:本文详细记录了对一个Android ARM64静态ELF文件中字符串加密机制的逆向分析过程。该ELF文件的所有字符串均被加密,无法通过常规strings命令或IDA直接识别。者通过分析发现,加密字符串存储在.rodata段,其解密所需信息(包括密文地址、长度和16位密钥)保存在.data.rel.ro段的40字节描述符中。核心解密函数sub_10F408采用自反的双pass流密码算法,结合固定密钥KEY_TERM(由.data段24字节数据计算得出),实现字节级非线性、位置与长度相关的加密。文章还复现了完整的Python解密脚本,并揭示了该保护机制的本质为代码混淆而非强加密,最终成功批量解密全部956条字符串,暴露程真实行为,如shell命令模板、设备标识篡改、网络重置等操。此外,文中还提及未启用的自定义壳框架及其反dump设计。; 适合人群:具备逆向工程基础的安全研究人员、二进制分析人员及对ELF保护技术感兴趣的开发者。; 使用场景及目标:①学习ELF二进制中字符串加密的典型实现方式与逆向突破口;②掌握从结构识别、函数追踪到算法还原的完整逆向流程;③理解“绑定二进制”的完整性校验设计及其局限性;④实践编写IDAPython脚本自动化提取与解密敏感数据。; 阅读建议:此资源以实战案例驱动,不仅展示技术细节,更强调逆向思维与验证方法,建议读者结合IDA调试环境,逐步跟随文中步骤进行动态分析与算法验证,深入理解每一步的推理依据。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值