CF1332D Walk on Matrix
90个测试点好评
简要题意:
求一个矩阵走过的最大 \(\&\) 和(只能往右、下走),然后有这样一个程序:
需要你 \(\text{hack}\) 掉这个程序,即构造一组数据,使得正确答案比该程序的答案正好大 \(k\).
首先我们要知道这个程序为什么是错的。
假设不需要相差 \(k\),随便构造一组:
3 1
2 3
0 1
你可以看到,该程序的 \(\text{dp}\) 数组为:
\(1\) | \(2\) | |
---|---|---|
\(1\) | \(dp_{1,1} = 3\) | \(dp_{1,2} = 3 \& 1 = 1\) |
\(2\) | \(dp_{2,1} = 3 \& 2 = 2\) | \(dp_{2,2} = \max(2 \& 3 ,1 \& 3) = \max(2 , 1) = 2\) |
\(3\) | \(dp_{3,1} = 2 \& 0 = 0\) | \(dp_{2,3} = \max(0 \& 1 , 2 \& 1) = \max(0 , 0) = 0\) |
完全错误。因为 \(dp_{2,2}\) 保存了最优值,但是因为
\(1 \& 1 > 2 \& 1\),所以 不满足最优子结构,即每个子结构最优不一定当前状态最优。
hack 也是一门艺术
那么如何让它正好相差 \(k\) 呢?
我们以上面的例子为例,构造 \(2 \times 3\) 的矩阵。
a b
c d
e f
首先我们让 \(e=0\),直接废掉 \(a \rightarrow c \rightarrow e \rightarrow f\) 这条路。
(因为 \(k \& 0 = 0\),路径为 \(0\) 不可能被 \(\texttt{dp}\) 选或作为答案)
那么,还剩下 \(a \rightarrow b \rightarrow d \rightarrow f\) 和 \(a \rightarrow c \rightarrow d \rightarrow f\) 两条路。
如何误导 \(\texttt{dp}\) 呢?hack怪癖:必须让它的答案和正确答案相差我固定的这个值
然后假设正确答案为 \(k\),而它的答案为 \(0\),并进一步设 \(f = k\).
此时我们构造这样一组数据 (\(t = \log_2^k\))
|\(2^{t+2}-1\)|\(k\)|
|--------|-----------|------------|
|\(2^{t+1}\) | \(2^{t+2}-1\)|
|\(0\)|\(k\)|
下面介绍原因。在上图中:
\(a \& b \& d = a \& d \& b = b = k\)(\(a \& d = d\),而结果全是 \(1\),所以 \(k\) 的原来 \(1\) 位被保留,而 \(0\) 仍是 \(0\))
而 \(2^{t+1}\)在二进制中,为 \(1\) 后面 \(t+1\) 个 \(0\),比 \(k\) 多一位,那么错误的程序会选择 \(2^{t+1}\).
然后 \((2^{t+1}) \& k = 0\),而正确的答案是 \(k \& k = k\).
hack人也要讲技巧对不对
你只要想,你的同学写这个程序A了题,然后你发现正解一定要把她 hack,然后这题就很有趣味不是么
\(log_2^k\) 正好是 \(\text{c++}\) 库函数。
时间复杂度:\(O(1)\).
实际得分:\(100pts\).
#pragma GCC optimize(2)
#include<bits/stdc++.h>
using namespace std;
inline int read(){char ch=getchar();int f=1;while(ch<'0' || ch>'9') {if(ch=='-') f=-f; ch=getchar();}
int x=0;while(ch>='0' && ch<='9') x=(x<<3)+(x<<1)+ch-'0',ch=getchar();return x*f;}
int main(){
int k=read(),t=log2(k);
printf("3 2\n");
printf("%d %d\n",(1<<(t+2))-1,k); //位运算 1<<n 就是 2 的 n 次方
printf("%d %d\n",(1<<(t+1)),(1<<(t+2))-1);
printf("0 %d\n",k);
return 0;
}