image frame

375的养老院

F - Trees and XOR Queries Again

上课闲着无聊娱乐看了一眼,太典了,没忍住写了下

本质上就是问树上一条链上能不能xor出一个给定值,2e5级别

众所周知,线性基可以解决任意个数能不能xor出给定值

众所周知,线性基可以通过B^2的复杂度合并,所以你甚至可以上树剖,每个线段树的节点维护一个线性基,那么这样是O(nlogn^2B^2),可能也能过吧,IDK

当然其实这个问题非常老套,有一个也许稍微冷门一点但是依旧广为人知的事情就是线性基是可以替换的,把问题退化到序列上的区间查询,对于一个以r为右端点的线性基,我们肯定希望基里面的元素尽可能靠右,所以我们可以通过替换一直更新线性基,让线性基更“优秀”,那么迁移到树上其实就是希望元素的深度尽可能深,对于一个查询,我们把(u,v)两个节点的线性基O(B^2)合并,那么我们真正的基就是合并后的基中深度不比LCA小的那些元素,然后就直接查询就行了,O(nlogn + nB^2)

可能这个是最优做法,如果你写点分治啥的遇到在线就不行了,复杂度也是最低了感觉

为什么突然想写了,因为感觉真的太典了,感觉出了好几次了,但是竟然在CF还是见到了,心血来潮写了

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
#include <bits/stdc++.h>
#include <random>

#define MP make_pair
#define pb push_back
#define ls(x) (x << 1)
#define rs(x) (x << 1 | 1)

using namespace std;
using LL = long long;
using ULL = unsigned long long;

const int maxn = 4e5 + 5;
const int maxk = 22;

int n;
int A[maxn];
int dep[maxn];
int fa[maxn][maxk];
vector<int> E[maxn];

struct linear_base{
int base[maxk];
int dep[maxk];

void insert(int x, int d){
for(int i = maxk - 1; i >= 0; --i){
int b = ((x >> i) & 1);
if(b){
if(!base[i]){
base[i] = x;
dep[i] = d;
return;
}
else{
if(dep[i] < d){
swap(dep[i], d);
swap(base[i], x);
}
}
x ^= base[i];
}
}
}

void operator = (const linear_base &rhs){
for(int i = 0; i < maxk; ++i){
base[i] = rhs.base[i];
dep[i] = rhs.dep[i];
}
}

int query(int x, int d){
for(int i = maxk - 1; i >= 0; --i){
int b = ((x >> i) & 1);
if(b && dep[i] >= d)
x ^= base[i];
}
return (x == 0);
}

}B[maxn], tmp_base;

void merge(linear_base &a, linear_base &b)
{
tmp_base = a;
for(int i = 0; i < maxk; ++i){
tmp_base.insert(b.base[i], b.dep[i]);
}
}

void dfs(int x, int fr)
{
B[x] = B[fr];
B[x].insert(A[x], dep[x]);
for(auto &v : E[x]){
if(v == fr) continue;
dep[v] = dep[x] + 1;
fa[v][0] = x;
dfs(v, x);
}


}

int lca(int x, int y)
{
if(dep[x] < dep[y]) swap(x, y);
int d = dep[x] - dep[y];
for(int i = maxk - i; i >= 0; --i){
if(((d >> i) & 1)){
x = fa[x][i];
}
}
if(x == y) return x;
for(int i = maxk - 1; i >= 0; --i){
if(fa[x][i] != fa[y][i]){
x = fa[x][i], y = fa[y][i];
}
}
return x == y ? x : fa[x][0];
}

int main()
{
ios::sync_with_stdio(0);
cin.tie(0);

cin >> n;
for(int i = 1; i <= n; ++i){
cin >> A[i];
}
for(int i = 1; i <= n - 1; ++i){
int a, b;
cin >> a >> b;
E[a].pb(b), E[b].pb(a);
}

dfs(1, 0);
for(int j = 1; j <= maxk - 1; ++j){
for(int i = 1; i <= n; ++i){
fa[i][j] = fa[fa[i][j - 1]][j - 1];
}
}

int Q = 0;
cin >> Q;
while(Q--){
int a, b, c;
cin >> a >> b >> c;
merge(B[a], B[b]);
int l = lca(a, b);
int ans = tmp_base.query(c, dep[l]);
if(ans) cout << "YES\n";
else cout << "NO\n";
}

return 0;
}

一些自用的东西

记忆力不太好啊,老是忘记,只能记一下了

排序

带索引排序

1
index = np.argsort(list)

vscode:justmycode:false 无效

添加设置

1
"purpose":["debug-in-terminal"]

模板

开个坑写一下一些模板

长链剖分

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
#include <bits/stdc++.h>

#define pb emplace_back

using namespace std;
typedef long long LL;

const int maxn = 1e6 + 5;
const int INF = 1e9 + 7;

LL *now;
LL *F[maxn];
LL buf[maxn << 1];


int n;
vector<int> E[maxn];
int A[maxn], mi[maxn][21];
int son[maxn], dep[maxn], mx[maxn];
int lg[maxn];

template<typename T>
void read(T &x)
{
x = 0;
int fl = 1;
char ch = getchar();
while(!isdigit(ch) && ch != '-')
ch = getchar();
if(ch == '-')
fl = -1, ch = getchar();
while(isdigit(ch))
x = x * 10 + ch - '0', ch = getchar();
x *= fl;
}

void dfs1(int x, int fr)
{
for(auto &v : E[x]){
if(v == fr)
continue;
dep[v] = dep[x] + 1;
dfs1(v, x);
if(mx[v] > mx[son[x]])
son[x] = v;
mx[x] = max(mx[x], mx[v]);
}
mx[x] += 1;
}

int ask(int l, int r)
{
int t = lg[r - l + 1];
return min(mi[l][t], mi[r - (1 << t) + 1][t]);
}

void dp(int x, int fr)
{
F[x][0] = A[0];
if(son[x]){
F[son[x]] = F[x] + 1;
dp(son[x], x);
}

int _mx = 0;
for(auto &v : E[x]){
if(v == fr || v == son[x])
continue;
F[v] = now;
now += mx[v];
_mx = max(_mx, mx[v] + 1);
dp(v, x);
for(int j = 0; j < mx[v]; ++j){
F[x][j + 1] += F[v][j];
}

}
for(int j = 0; j <= _mx; ++j)
F[x][j] = min(F[x][j], 1LL * ask(j, dep[x] + j));
}

int main()
{
int T;
read(T);
while(T--){
read(n);
for(int i = 0; i <= now - buf; ++i)
buf[i] = 0;
for(int i = 2; i <= n; ++i)
lg[i] = lg[i >> 1] + 1;
for(int i = 1; i <= n; ++i)
E[i].clear(), son[i] = dep[i] = mx[i] = 0;
for(int i = 0; i <= n - 1; ++i){
read(A[i]);
mi[i][0] = A[i];
}

for(int j = 1; j <= lg[n]; ++j)
for(int i = 0; i + (1 << j) - 1 <= n - 1; ++i)
mi[i][j] = min(mi[i][j - 1], mi[i + (1 << (j - 1))][j - 1]);

for(int i = 1; i <= n - 1; ++i){
int u, v;
read(u), read(v);
E[u].pb(v), E[v].pb(u);
}

dfs1(1, 0);


now = buf;
F[1] = now;
now += mx[1];
dp(1, 0);

LL ans = 0;
for(int i = 0; i < mx[1]; ++i)
ans += F[1][i];
cout << ans << endl;
}
}

Bomb Lab

先给explode_bomb打个断点,防止爆炸了

Phase1

用到的一些命令

1
2
3
b *0x00000 (地址打断点)
x 0x00000 (查看的值)
x/s 0x000000 (确定是一个字符串,打印这个字符串)

image-20240915232513043

可以看到存在第二个参数应该是402400,然后我就把密码弄成402400就寄了

然后又调了会儿,看了下input,是一个char*,才反应过来0x402400是指针,用x/s查看字符串即可

Phase2

image-20240916001453238

起始条件,第一位为1,然后上一个数($rsp-4)*2 == ($rsp),所以就是一个等比数列

Phase3

image-20240916005117503

首先压栈了两个东西,看起来很像是要读入两个东西

然后比较了rax有没有>1,这里经过试验好像是输入个数

然后不允许值超过7,并且进行了某个跳转,看到下面有一堆的赋值,并且赋值完跳到400fbe,猜测就是输入不同的偏移值,会跳到不同部分,然后比较0xc+$rsp的值和刚才赋值的rax,我这里选择了第一个数是5,然后查看跳转到了400f98,也就是把rax赋值成了0xce,所以第二个数直接输入0xce(206)即可

Phase4

image-20240916112742116

func4是一个奇怪的递归函数,我们要命中400ff2这一行,相当于要让rcx == rdi,rcx经过分析可以发现是一个类似于(l + r) >> 1的操作,这里多试几次就可以找出来哪些返回值会是0了,然后要求第二个输入是0,我这里一次成了(1 0),func4的逻辑比较奇怪,大致上是

1
2
3
4
5
6
7
def func4(int x, int y, int z)
{
int mid = (z - y) / 2 + x;
if (mid > x) return func4(x, y, mid - 1) * 2;
if (mid < x) return func4(x, mid + 1, z) * 2 + 1;
return 0;
}

我不太想去细推哪些值可以了

Phase5

image-20240916121010565

首先可以观察到这个密码应该长度是6

然后在最后和0x40245e比较,打开0x40245e可以看到是

1
flyers

直接输入密码后,去查看$rsp+0x10的值发现六个字符变化了,大概就是做了某些后处理,然后再观察一下看到给每个index加了一个偏移量0x4024b0,然后取出其中的字符,比对一下可以得到如下对应关系

1
2
maduiersnfotvbylSo you think you can stop the bomb with ctrl-c, do you?
abcdefghijklmno

所以ionefg对应flyers

Phase3

前两部分比较简单,分别是要求六个数不能有相等的,然后arr[i] = 7 - arr[i]

image-20240916191506024

从小到大分别为216543,这里我没看到node标志被坑了非常久,以及需要把x的输出调整w(也就是4字),我看他直接+8就用g,然后低8位直接完全看不懂是什么,换w后就明朗了

image-20240916192947751

这一段就是要求递减了,所以我们的密码应该是节点的index,然后要求节点的value逐渐递减

然后要记得我们的index做过翻转的(7-x)

答案是4 3 2 1 6 5

Secret Phase

image-20240916202325451

可以知道输入的数<0x3e8,然后要求输出为2,接下来看func7

image-20240916202456714

1
2
3
*rdi == rdx : ret -> 0
*rdi > rdx : ret *= 2
*rdi < rdx : ret = ret * 2 + 1

凑一个2出来即可,0->1(2+1)->2(2)

答案是22

WSL下无法使用core dump的解决方案

我正在做一个小项目,使用的WSL,但是在学习gdb的时候遇到了一个问题,就是我的程序没有core dumped,对于以下c程序

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
int actual_calc(int a, int b){
int c;
c=a/b;
return 0;
}

int calc(){
int a;
int b;
a=13;
b=0;
actual_calc(a, b);
return 0;
}

int main(){
calc();
return 0;
}

使用

1
gcc -ggdb test.c -o test.out

然后设定

1
2
3
4
5
if ! grep -qi 'kernel.core_pattern' /etc/sysctl.conf; then
sudo sh -c 'echo "kernel.core_pattern=core.%p.%u.%s.%e.%t" >> /etc/sysctl.conf'
sudo sysctl -p
fi
ulimit -c unlimited

然后添加以下进/etc/security/limits.conf

1
2
* soft core unlimited
* hard core unlimited

之后运行test.out,但是输出

1
Floating point exception

理论上应该有

1
Floating point exception(core dumped)

后面在WSL的issue里面找到了https://github.com/microsoft/WSL/issues/1262,通过设置

1
sudo sysctl -w kernel.core_pattern=/tmp/core

可以生成core文件(不然就是空的),现在可以看到core文件正常了

1
2
file /tmp/core.890
/tmp/core.890: ELF 64-bit LSB core file, x86-64, version 1 (SYSV), SVR4-style, from './test.out', real uid: 1002, effective uid: 1002, real gid: 1002, effective gid: 1002, execfn: './test.out', platform: 'x86_64'

编译后程序迁移问题

最近有一个需求,把一个在高版本机器上编译好的程序迁移到低版本上,由于我是C++小白完全不懂怎么解决,折腾了好久,写个博客记录一下

把动态库一起放上去

先查看一下依赖

1
2
3
4
5
6
7
8
linux-vdso.so.1 (0x00007ffdf57fc000)
libdl.so.2 => /lib/x86_64-linux-gnu/libdl.so.2 (0x00007f45cd3d5000)
libpthread.so.0 => /lib/x86_64-linux-gnu/libpthread.so.0 (0x00007f45cd3b2000)
libstdc++.so.6 => /lib/x86_64-linux-gnu/libstdc++.so.6 (0x00007f45cba1e000)
libm.so.6 => /lib/x86_64-linux-gnu/libm.so.6 (0x00007f45cd263000)
libgcc_s.so.1 => /lib/x86_64-linux-gnu/libgcc_s.so.1 (0x00007f45cd248000)
libc.so.6 => /lib/x86_64-linux-gnu/libc.so.6 (0x00007f45cb82c000)
/lib64/ld-linux-x86-64.so.2 (0x00007f45cd3e4000)

然后写一个bash脚本把所有依赖拷进一个文件夹

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
#!/bin/sh

# List of libraries to copy
libs="/lib/x86_64-linux-gnu/libstdc++.so.6
/lib/x86_64-linux-gnu/libreadline.so.8
/lib/x86_64-linux-gnu/libdl.so.2
/lib/x86_64-linux-gnu/libpthread.so.0
/usr/local/lib/yosys/libyosys.so
/lib/x86_64-linux-gnu/libm.so.6
/lib/x86_64-linux-gnu/libgcc_s.so.1
/lib/x86_64-linux-gnu/libc.so.6
/lib/x86_64-linux-gnu/libtinfo.so.6
/lib/x86_64-linux-gnu/libffi.so.7
/lib/x86_64-linux-gnu/libz.so.1
/lib/x86_64-linux-gnu/libtcl8.6.so"

# Copy libraries to the current directory
for lib in $libs; do
cp $lib .
done

之后再写一个脚本把所有的依赖路径换成想在目标服务器上放置的路径,这里用的工具是patchelf

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
#!/bin/sh

# List of libraries to copy
libs="/lib/x86_64-linux-gnu/libstdc++.so.6
/lib/x86_64-linux-gnu/libreadline.so.8
/lib/x86_64-linux-gnu/libdl.so.2
/lib/x86_64-linux-gnu/libpthread.so.0
/usr/local/lib/yosys/libyosys.so
/lib/x86_64-linux-gnu/libm.so.6
/lib/x86_64-linux-gnu/libgcc_s.so.1
/lib/x86_64-linux-gnu/libc.so.6
/lib/x86_64-linux-gnu/libffi.so.7
/lib/x86_64-linux-gnu/libz.so.1
/lib/x86_64-linux-gnu/libtcl8.6.so"

# Extract library name from path and prepend './dll/'
for lib_path in $libs; do
extracted_lib=$(basename $lib_path)

patchelf --replace-needed $extracted_lib '$ORIGIN/'$extracted_lib ./workflow
echo "patchelf --replace-needed $extracted_lib ./dll/$extracted_lib ./workflow"
done

之后在目标服务器上修改rpath

1
patchelf --set-rpath your/rpath your/program

同时,不要忘记修改interpreter,不然ldd看依赖即使正确也会报段错误

1
patchelf --set-interpreter your/interpreter your/program

静态链接

我的程序有一些奇奇怪怪的库用了glibc的动态库,导致我没法用这条路,虽然也不太推荐就是了

使用如下方式,需要注意的是,static需要写在dynamic的前面,高版本gcc还可以用-static的选项

1
2
-Wl,-Bstatic -lstdc++
-Wl,-Bdynamic -lm -ldl -lpthread

其他

  • 动态库的SONAME设置有问题
  • objdump -p + grep 查看设置有没有问题

笛卡尔树

笛卡尔树

用处

复建,连简单的数据结构都不记得了

笛卡尔树维护的是树最右边的一条链

常用于有限制条件下的求极值

代码

简单来说,对于小根堆形式的笛卡尔树,就是维护的增的单调栈

1
2
3
4
5
6
7
8

for(int i=1;i<=n;++i){
while(tp&&A[stk[tp]]>A[i])
L[i]=stk[tp--];
if(tp)
R[stk[tp]]=i;
stk[++tp]=i;
}

题目

不记得之前写过什么题了,有遇到再更新

  • Copyrights © 2015-2024 0375w