90个测试点好评
简要题意:
求一个矩阵走过的最大 & \& & 和(只能往右、下走),然后有这样一个程序:
initialize d p i , j ← 0 , for all 0 ≤ i ≤ n , 0 ≤ j ≤ m , except d p 0 , 1 ← a 1 , 1 \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} initialize dpi,j←0,for all 0≤i≤n,0≤j≤m,except dp0,1←a1,1
for i = 1 → n to do \texttt{for} \space i =1 \rightarrow n \space \texttt{to} \space \texttt{do} for i=1→n to do
for j = 1 → m to do \texttt{for} \space j =1 \rightarrow m \space \texttt{to} \space \texttt{do} for j=1→m to do
d p i , j ← max ( d p i − 1 , j & a i , j , d p i , j − 1 & a i , j ) dp_{i,j} \gets \max(dp_{i-1,j} \& a_{i,j} , dp_{i,j-1} \& a_{i,j}) dpi,j←max(dpi−1,j&ai,j,dpi,j−1&ai,j)
end for \texttt{end for} end for
end for \texttt{end for} end for
S ← d p n , m S \gets dp_{n,m} S←dpn,m
需要你 hack \text{hack} hack 掉这个程序,即构造一组数据,使得正确答案比该程序的答案正好大 k k k.
首先我们要知道这个程序为什么是错的。
假设不需要相差 k k k,随便构造一组:
3 1
2 3
0 1
你可以看到,该程序的 dp \text{dp} dp 数组为:
1 1 1 | 2 2 2 | |
---|---|---|
1 1 1 | d p 1 , 1 = 3 dp_{1,1} = 3 dp1,1=3 | d p 1 , 2 = 3 & 1 = 1 dp_{1,2} = 3 \& 1 = 1 dp1,2=3&1=1 |
2 2 2 | d p 2 , 1 = 3 & 2 = 2 dp_{2,1} = 3 \& 2 = 2 dp2,1=3&2=2 | d p 2 , 2 = max ( 2 & 3 , 1 & 3 ) = max ( 2 , 1 ) = 2 dp_{2,2} = \max(2 \& 3 ,1 \& 3) = \max(2 , 1) = 2 dp2,2=max(2&3,1&3)=max(2,1)=2 |
3 3 3 | d p 3 , 1 = 2 & 0 = 0 dp_{3,1} = 2 \& 0 = 0 dp3,1=2&0=0 | d p 2 , 3 = max ( 0 & 1 , 2 & 1 ) = max ( 0 , 0 ) = 0 dp_{2,3} = \max(0 \& 1 , 2 \& 1) = \max(0 , 0) = 0 dp2,3=max(0&1,2&1)=max(0,0)=0 |
完全错误。因为 d p 2 , 2 dp_{2,2} dp2,2 保存了最优值,但是因为
1 & 1 > 2 & 1 1 \& 1 > 2 \& 1 1&1>2&1,所以 不满足最优子结构,即每个子结构最优不一定当前状态最优。
hack 也是一门艺术
那么如何让它正好相差 k k k 呢?
我们以上面的例子为例,构造 2 × 3 2 \times 3 2×3 的矩阵。
a b
c d
e f
首先我们让 e = 0 e=0 e=0,直接废掉 a → c → e → f a \rightarrow c \rightarrow e \rightarrow f a→c→e→f 这条路。
(因为 k & 0 = 0 k \& 0 = 0 k&0=0,路径为 0 0 0 不可能被 dp \texttt{dp} dp 选或作为答案)
那么,还剩下 a → b → d → f a \rightarrow b \rightarrow d \rightarrow f a→b→d→f 和 a → c → d → f a \rightarrow c \rightarrow d \rightarrow f a→c→d→f 两条路。
如何误导
dp
\texttt{dp}
dp 呢?hack怪癖:必须让它的答案和正确答案相差我固定的这个值
然后假设正确答案为 k k k,而它的答案为 0 0 0,并进一步设 f = k f = k f=k.
此时我们构造这样一组数据 ( t = log 2 k t = \log_2^k t=log2k)
2 t + 2 − 1 2^{t+2}-1 2t+2−1 | k k k |
---|---|
2 t + 1 2^{t+1} 2t+1 | 2 t + 2 − 1 2^{t+2}-1 2t+2−1 |
0 0 0 | k k k |
下面介绍原因。在上图中:
a & b & d = a & d & b = b = k a \& b \& d = a \& d \& b = b = k a&b&d=a&d&b=b=k( a & d = d a \& d = d a&d=d,而结果全是 1 1 1,所以 k k k 的原来 1 1 1 位被保留,而 0 0 0 仍是 0 0 0)
a & c & d = a & d & c = 2 t + 1 a \& c \& d = a \& d \& c = 2^{t+1} a&c&d=a&d&c=2t+1
而 2 t + 1 2^{t+1} 2t+1在二进制中,为 1 1 1 后面 t + 1 t+1 t+1 个 0 0 0,比 k k k 多一位,那么错误的程序会选择 2 t + 1 2^{t+1} 2t+1.
然后 ( 2 t + 1 ) & k = 0 (2^{t+1}) \& k = 0 (2t+1)&k=0,而正确的答案是 k & k = k k \& k = k k&k=k.
hack人也要讲技巧对不对
你只要想,你的同学写这个程序A了题,然后你发现正解一定要把她 hack,然后这题就很有趣味不是么
l o g 2 k log_2^k log2k 正好是 c++ \text{c++} c++ 库函数。
时间复杂度: O ( 1 ) O(1) O(1).
实际得分: 100 p t s 100pts 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;
}