D. Walk on Matrix(超详细讲了原理)


D. Walk on Matrix


标签

  • 构造

简明题意

  • 假设有一个数字矩阵,每次可以向下或向右,问从左上角走到右下角的数字和最多是多少,那么显然这是一个dp。
  • 现在不问你数字和,而是把路径上的所有数都&起来,如果还是dp显然是错的。假设dp的到的结果是x,正确答案是y。
  • 现在给定k,需要你构造一个矩阵,使得|y-x|=k

思路

  • 首先我们思考什么时候dp是错的。假设矩阵中有一个点(x,y)是10101.此时最多两种选择,从(x-1,y)或(x,y-1)转移过来。再假设(x-1,y)是10000,(x,y-1)是101,如下表
        ?    10000
       101   10101
  • 那么dp时选择(x-1,y)位置的10000,得到10000,选择(x,y-1)位置的101,得到的是101,根据dp,(x,y)点会选择10000而不是101.
  • 现在假设矩阵是这样的(也就是多了一项)
        ?    10000    ?
       101   10101   100
  • 10101的结果是10000,那么100选择左边时得到的是0。如果10101选择了101得到101,那么最后得到的结果就是100.由此可见,dp会优先选择使更高的位为1的数。
  • 我们可以抓住dp的这个弱点,使得dp结果是0,而正确结果是k。现在思考如何构造。
  • 上面的例子中,如果把第一行第三个数填0
       ?    10000    0
      101   10101   100
  • 那么不考虑左上角的? dp的结果就是0。现在考虑如何填写?使得dp的结果仍然是0.我们只需要使得10000和101异或上?值不变即可。显然?可以填成111111,使得10000和101都不变。
      10101   10000    0
       101    10101   100
  • 这样的矩阵,dp的答案是0.现在我们想想如何使正确答案为k。显然这里从左上到右下一共有3条路径,分别为
____
     |        这一条结果为0
      ____
  
|             
|_______        这一条结果非0

________
        |       这一条结果为0
        |
  • 由上可知我们只能操作第二条路径使得第二条路径的结果为k。
  • 利用刚才的思路,我们可以让k加上10···0(很大),使得某一步会错误地选择10···0从而异或出很大的10···0。下面这个三角就是,10···0+k选择上面的会形成10···0,而选择左边的k会形成k。
       ?      10···0
       k      10···0+k 
  • 那么我们可以再仿照最开始举例思路,继续填
      ?      10···0     0
      k      10···0+k   k
  • 这样,使得两种路径的&都是0,我们现在只需要确定左上角的?给根据上面的举例,?填成1111···111即可。
  • 当然也可以填10···0+k,只要使得?下面和右边的数不变就可以了。
  • 至于10···0具体是多少,题目说了k<=1e5,那么217>1e52^{17}>1e5,填2172^{17}即可。而左上角填21812^{18}-1就行(当然也能217+k2^{17}+k)。即:
      2^18-1   2^17     0
      k        2^17+k   k
  • 这里扩展一下,加入题目规定了矩阵的长宽,需要我们构造。那么我们可以按照下面的构造:
      2^18-1    0       0   0   0
      2^18-1    0       0   0   0
      2^18-1   2^17     0   0   0
      k        2^17+k   k   k   k

也就是竖着用0 0 0 k填充,横着用1111 0 0 0填充即可。


注意事项


总结

  • 题目告诉你什么方式是错的,那么就应该思考这种方法在哪种情况下是错的。

AC代码

#pragma GCC optimize(2)
#include<iostream>
#include<algorithm>
#include<cmath>
#include<cstring> 
#include<stack>
#include<map>
#include<queue>
#include<cstdio>	
#include<set>
#include<map>
#include<string>
using namespace std;

const int maxn = 1000 + 10;

void solve()
{
	int k;
	cin >> k;
	cout << "2 3" << endl;
	cout << ((1 << 18)-1) << " " << (1 << 17) <<" " << "0" << endl;
	cout << k << " " << ((1 << 17) + k) << " " << k << endl;
}
	
int main()
{
	//freopen("Testin.txt", "r", stdin);
	solve();
	return 0;
}

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章