cdq 三维偏序应用 / P4169 [Violet] 天使玩偶/SJY摆棋子

最近学了 cdq 分治想来做做这道题,结果被有些毒瘤的代码恶心到了。 /ll

题目大意:一开始给定一些平面中的点。然后给定一些修改和询问:

  • 修改:增加一个点。
  • 询问:给定一个点,求离这个点最近(定义为曼哈顿距离最小)的点离这个点的曼哈顿距离。(注意这个点并不加入平面)

其他细节详见 题目传送门

显然一开始给定的点可以看作是一开始的修改。现在就只有修改和查询了。

发现这里的曼哈顿距离有两个绝对值,考虑拆一下绝对值,显然我们可以拆出这个式子:

  • 如果 A x ≤ B x , A y ≤ B y A_x \le B_x,A_y \le B_y AxBx,AyBy,也就是 A A A 在平面上面的距离在 B B B 的左下方,则 A A A B B B 的距离 dist ( A , B ) = ( B x + B y ) − ( A x + A y ) \text{dist}(A,B) = (B_x+B_y)-(A_x+A_y) dist(A,B)=(Bx+By)(Ax+Ay)

那么该怎么处理其他方面的点呢:左上方、右下方、右上方???从左下方通过坐标变换旋转一下就可以同样处理了。所以我们现在只需要处理 A A A B B B 的左下方的情况即可!

发现在这个式子里面, A A A B B B 是独立的。如果 B B B 是一个询问的点,那么在其左下方的点中( A x ≤ B x , A y ≤ B y A_x \le B_x,A_y \le B_y AxBx,AyBy),若要使其询问的答案最小,显然需要使 A x + A y A_x+A_y Ax+Ay 的值最大,这个是可以预处理出来的。

而且还需要保证 A A A 的出现时间在 B B B 的前面。

这个时候我们梳理一下在 A A A B B B 左下方的这种情况下, A A A B B B 产生贡献的条件当且仅当:

  • A t ≤ B t A_t \le B_t AtBt,也就是 A A A B B B 前面出现。
  • A x ≤ B x , A y ≤ B y A_x \le B_x,A_y \le B_y AxBx,AyBy,也就是 A A A B B B 左下方。

这不就是三维偏序吗!!!!所以直接使用 cdq 三维偏序即可。

代码可能有点难写,但是有很长一段都是很基础的。

#include <bits/stdc++.h>
using namespace std;
const int N = 1000010;

struct que {
	bool f;
	int id, x, y;
} ori[N], val[N], a[N];
int n, m;
int mx = 0, my = 0;
int ans[N];

bool cmp1(que x, que y) {
	if (x.id != y.id)
		return x.id < y.id;
	if (x.x != y.x)
		return x.x < y.x;
	return x.y < y.y;
}

bool cmp2(que x, que y) {
	return x.x < y.x;
}

struct BIT {
	int tree[N];
	void clear(int pos) {
		for (; pos <= my; pos += pos & -pos)
			if (tree[pos])
				tree[pos] = 0;
			else
				return ;
	}
	void add(int pos, int val) {
		for (; pos <= my; pos += pos & -pos)
			tree[pos] = max(tree[pos], val);
	}
	int query(int pos) {
		int ans = 0;
		for (; pos; pos -= pos & -pos)
			ans = max(ans, tree[pos]);
		return ans;
	}
} st;

void cdq(int l, int r) {
	if (l + 1 == r)
		return ;
	int mid = (l + r) / 2;
	cdq(l, mid);
	sort(val + l, val + mid, cmp2);
	sort(val + mid, val + r, cmp2);
	int pos = l;
	for (int i = mid; i < r; i++) {
		if (val[i].f)
			continue;
		for (; pos < mid && val[pos].x <= val[i].x; pos++)
			if (val[pos].f)
				st.add(val[pos].y, val[pos].x + val[pos].y);
		int x = st.query(val[i].y);
		if (x > 0)
			ans[val[i].id] = min(ans[val[i].id], val[i].x + val[i].y - x);
	}
	for (int i = l; i <= pos; i++)
		st.clear(val[i].y);
	copy(ori + l, ori + r, val + l);
	cdq(mid, r);
}

int main() {
	cin >> n >> m;
	for (int i = 1; i <= n; i++) {
		int x, y;
		cin >> x >> y, x++, y++;
		ori[i] = {1, i, x, y};
		mx = max(mx, x), my = max(my, y);
	}
	for (int i = 1; i <= m; i++) {
		int op, x, y;
		cin >> op >> x >> y;
		x++, y++;
		mx = max(mx, x), my = max(my, y);
		if (op == 1)
			ori[i + n] = {1, i + n, x, y};
		else
			ori[i + n] = {0, i + n, x, y};
	}
	n += m, mx++, my++;
	for (int i = 1; i <= n; i++)
		ans[i] = 1e9;
	copy(ori + 1, ori + n + 1, a + 1);
	//cdq
	sort(ori + 1, ori + n + 1, cmp1);
	copy(ori + 1, ori + n + 1, val + 1);
	cdq(1, n + 1);
	//---
	copy(a + 1, a + n + 1, ori + 1);
	for (int i = 1; i <= n; i++)
		ori[i].x = mx - ori[i].x;
	sort(ori + 1, ori + n + 1, cmp1);
	copy(ori + 1, ori + n + 1, val + 1);
	cdq(1, n + 1);
	//---
	copy(a + 1, a + n + 1, ori + 1);
	for (int i = 1; i <= n; i++)
		ori[i].y = my - ori[i].y;
	sort(ori + 1, ori + n + 1, cmp1);
	copy(ori + 1, ori + n + 1, val + 1);
	cdq(1, n + 1);
	//---
	copy(a + 1, a + n + 1, ori + 1);
	for (int i = 1; i <= n; i++)
		ori[i].x = mx - ori[i].x, ori[i].y = my - ori[i].y;
	sort(ori + 1, ori + n + 1, cmp1);
	copy(ori + 1, ori + n + 1, val + 1);
	cdq(1, n + 1);
	for (int i = 1; i <= n; i++)
		if (a[i].f == 0)
			cout << ans[i] << endl;
	return 0;
}

### 实现ret2libc攻击的替代方案 在缺乏`/bin/sh`字符串和`system`函数的情况下,仍然可以通过调用其他可用的库函数来实现类似的控制流劫持效果[^1]。 #### 使用execve代替system 如果目标进程中存在指向`"/bin/bash"`或其他可执行文件路径的有效指针,则可以尝试构建对`execve`的调用。此方法依赖于找到合适的参数布局以及可能存在的环境变量或已加载共享对象中的有效路径字符串。 ```c // 假设已经获得了execve@got.plt处的地址 void *execve_addr; char *argv[] = { "/path/to/shell", NULL }; char *envp[] = { "PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin", NULL }; __asm__( "push %0\n\t" "movl $0, %%ebx\n\t" /* filename */ "lea 8(%%esp), %%ecx\n\t" /* argv */ "lea 16(%%esp), %%edx\n\t" /* envp */ "call *%1" : : "r"(argv[0]), "r"(execve_addr) : "%ebx", "%ecx", "%edx"); ``` #### 利用链式gadget组合现有功能 当既无合适命令行解释器也找不到相应API入口时,可通过ROP(Return-Oriented Programming)技术搜集多个短小精悍的小片段(gadgets),这些片段通常来自标准C库(`libc`)内部未被保护的部分。通过精心安排堆栈上的返回地址序列,最终达到创建新进程的目的。 例如,在某些情况下可以从`.text`段内发现如下有用指令: - `pop eax; ret`: 将下一个操作数弹入EAX寄存器并跳转到下一条指令; - `int 0x80`: 发起Linux系统调用中断请求; 结合上述两个 gadget 可以形成简单的 execve 调用框架: ```nasm xor eax, eax ; 清零 EAX 寄存器 (syscall number for execve is 0xb or 11 decimal) push eax ; 推送NULL作为第三个参数(envp[]) push <address_of_argv> ; 推送第二个参数(argv[]) push <address_of_filename>; 推送第一个参数(filename="/bin/bash") mov ebx, esp ; 设置 EBX 指向我们的 arguments list cdq ; CDQ 扩展 EAX 至 EDX:EAX 形成 sign extension of the value in EAX into EDX. int 0x80 ; 触发 syscall interrupt with int 0x80 instruction ``` 为了成功实施这种类型的攻击,需要满足几个前提条件:能够读取内存空间以定位必要的 gadgets 地址、有足够的权限绕过NX(non-executable stack)防护机制,并且能够在不受干扰的情况下修改运行期堆栈结构。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值