向量

题目描述

给定一棵 nnn 个节点的树,点的标号为 1…n1 \dots n1n ,边有边权。
d(u,v)d(u, v)d(u,v)uuuvvv 的路径上边的权值和,对于每个节点 uuu ,你需要给出一个 mmm 维向量 pu={pu,1,…,pu,m}p_u = \{p_{u,1}, \dots, p_{u,m}\}pu={pu,1,,pu,m} ,使得对于任意点对 u,vu,vu,v ,满足 d(u,v)=max{∣pu,i−pv,i∣}d(u, v) = max\{|p_{u,i} − p_{v,i}|\}d(u,v)=max{pu,ipv,i}

数据范围

2≤n≤10002 \le n \le 10002n10001≤wi≤1051 \le w_i \le 10^51wi105m≤16m \le 16m16

题解

怎么想到点分的不知道。

考虑一个点分中心 rootrootroot ,把子树尽量按 sizesizesize 均分成两部分,然后使用一维向量,将 AAA 集合的值设为 deepdeepdeepBBB 集合设为 −deep-deepdeeprootrootroot 设为 000 ,然后将 rootrootroot 分别加入两边集合继续递归下去得到两个不同的向量,然后将其中一个向量平移成另一个向量即可,要把向量上所属集合也要平移。

代码

#include <bits/stdc++.h>
using namespace std;
const int N=1005,M=N<<1;
int n,hd[N],V[M],nx[M],W[M],t,S[N],sz[N],rt;
int o,vis[N],b[N],son[N],f[N][18],g[18][18];
bool cmp(int x,int y){return sz[x]>sz[y];}
void add(int u,int v,int w){
	nx[++t]=hd[u];V[hd[u]=t]=v;W[t]=w;
}
void Sz(int x,int fr){
	sz[x]=1;
	for (int i=hd[x];i;i=nx[i])
		if (V[i]!=fr && !vis[V[i]])
			Sz(V[i],x),sz[x]+=sz[V[i]];
}
void Rt(int x,int fr){
	son[x]=o-sz[x];
	for (int i=hd[x];i;i=nx[i])
		if (V[i]!=fr && !vis[V[i]])
			Rt(V[i],x),son[x]=max(son[x],sz[V[i]]);
	if (son[x]<son[rt]) rt=x;
}
void push(int x,int fr,int v){
	vis[x]=v;
	for (int i=hd[x];i;i=nx[i])
		if (V[i]!=fr && !vis[V[i]])
			push(V[i],x,v);
}
void clr(int x,int fr,int v){
	vis[x]=0;
	for (int i=hd[x];i;i=nx[i])
		if (V[i]!=fr && vis[V[i]]==v)
			clr(V[i],x,v);
}
void gx(int x,int fr,int v){
	for (int i=v+1;i<=16;i++)
		f[x][i]+=g[v][i];f[x][v]=-f[x][v];
	for (int i=hd[x];i;i=nx[i])
		if (V[i]!=fr && !vis[V[i]])
			gx(V[i],x,v);
}
void down(int x,int fr,int v,int w){
	f[x][v]=w;
	for (int i=hd[x];i;i=nx[i])
		if (V[i]!=fr && !vis[V[i]])
			down(V[i],x,v,w+W[i]);
}
void work(int x,int v){
	o=sz[x];rt=t=0;Rt(x,0);x=rt;Sz(x,0);
	for (int i=hd[x];i;i=nx[i]) if (!vis[V[i]])
		S[++t]=V[i],down(V[i],x,v,W[i]);
	if (sz[x]<=2) return;
	sort(S+1,S+t+1,cmp);int w[2]={0,0};
	for (int j,i=1;i<=t;i++)
		j=w[0]>w[1],w[b[S[i]]=j]+=sz[S[i]];
	for (int i=hd[x];i;i=nx[i])
		if (!vis[V[i]] && b[V[i]]) push(V[i],x,v);
	sz[x]=w[0]+1;work(x,v+1);
	for (int i=v+1;i<=16;i++) g[v][i]=f[x][i],f[x][i]=0;
	for (int i=hd[x];i;i=nx[i]){
		if (!vis[V[i]]) push(V[i],x,v);
		else if (vis[V[i]]==v) clr(V[i],x,v);
	}
	sz[x]=w[1]+1;work(x,v+1);
	for (int i=v+1;i<=16;i++)
		g[v][i]-=f[x][i],f[x][i]+=g[v][i];
	for (int i=hd[x];i;i=nx[i])
		if (vis[V[i]]==v) clr(V[i],x,v);
		else if (!vis[V[i]]) gx(V[i],x,v);
}
int main(){
	cin>>n;son[0]=1e9;
	for (int i=1,x,y,z;i<n;i++)
		scanf("%d%d%d",&x,&y,&z),
		add(x,y,z),add(y,x,z);
	Sz(1,0);work(1,1);puts("16");
	for (int i=1;i<=n;i++)
		for (int j=1;j<=16;j++)
			printf("%d",f[i][j]),
			putchar(j<16?' ':'\n');
	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、付费专栏及课程。

余额充值