標籤
- 構造
簡明題意
- 假設有一個數字矩陣,每次可以向下或向右,問從左上角走到右下角的數字和最多是多少,那麼顯然這是一個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,那麼,填即可。而左上角填就行(當然也能)。即:
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;
}