CF1332D Walk on Matrix

CSDN同步

原题链接

90个测试点好评

简要题意:

求一个矩阵走过的最大 \(\&\) 和(只能往右、下走),然后有这样一个程序:

\[\texttt{initialize} \space dp_{i,j} \gets 0 , \texttt{for all} \space 0 \leq i \leq n , 0 \leq j \leq m ,\text{except} \space dp_{0,1} \gets a_{1,1} \]

\[\texttt{for} \space i =1 \rightarrow n \space \texttt{to} \space \texttt{do} \]

\[\texttt{for} \space j =1 \rightarrow m \space \texttt{to} \space \texttt{do} \]

\[dp_{i,j} \gets \max(dp_{i-1,j} \& a_{i,j} , dp_{i,j-1} \& a_{i,j}) \]

\[\texttt{end for} \]

\[\texttt{end for} \]

\[S \gets dp_{n,m} \]

需要你 \(\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\)

\[a \& c \& d = a \& d \& c = 2^{t+1} \]

\(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;
}

posted @ 2020-04-03 10:05  bifanwen  阅读(166)  评论(0编辑  收藏  举报