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

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