题目大意
给定两个非负整数 x x x ,y y y 。找到两个非负整数p p p 和q q q ,使得p & q = 0 p\;\&\;q=0 p & q = 0 ,且∣ x − p ∣ + ∣ y − q ∣ |x-p|+|y-q| ∣ x − p ∣ + ∣ y − q ∣ 最小化。这里,& \& & 表示按位与操作 。
You are given two non-negative integers x x x , y y y . Find two non-negative integers p p p and q q q such that p & q = 0 p\;\&\;q=0 p & q = 0 , and ∣ x − p ∣ + ∣ y − q ∣ |x-p|+|y-q| ∣ x − p ∣ + ∣ y − q ∣ is minimized. Here, & \& & denotes the bitwise AND operation .
Input
Each test contains multiple test cases. The first line contains the number of test cases t t t (1 ≤ t ≤ 1 0 4 1 \le t \le 10^4 1 ≤ t ≤ 1 0 4 ). The description of the test cases follows.
The only line of each test case contains two non-negative integers x x x and y y y (0 ≤ x , y < 2 30 0 \le x,y < 2^{30} 0 ≤ x , y < 2 3 0 ).
Output
For each test case, output two non-negative integers p p p and q q q you found. If there are multiple pairs of p p p and q q q satisfying the conditions, you may output any of them.
It can be proven that under the constraints of the problem, any valid solution satisfies max ( p , q ) < 2 31 \max(p,q)<2^{31} max ( p , q ) < 2 3 1 .
1 2 3 4 5 6 7 8 7 0 0 1 1 3 6 7 11 4 4 123 321 1073741823 1073741822
1 2 3 4 5 6 7 0 0 2 1 3 8 6 9 4 3 128 321 1073741824 1073741822
Note
In the first test case, one valid pair would be p = 0 p=0 p = 0 and q = 0 q=0 q = 0 , as 0 & 0 = 0 0\,\&\,0=0 0 & 0 = 0 and ∣ x − p ∣ + ∣ y − q ∣ = ∣ 0 − 0 ∣ + ∣ 0 − 0 ∣ = 0 |x-p|+|y-q|=|0-0|+|0-0|=0 ∣ x − p ∣ + ∣ y − q ∣ = ∣ 0 − 0 ∣ + ∣ 0 − 0 ∣ = 0 is the minimum over all pairs of p p p and q q q .
In the third test case, one valid pair would be p = 3 p=3 p = 3 and q = 8 q=8 q = 8 , as 3 & 8 = 0 3\,\&\,8=0 3 & 8 = 0 and ∣ x − p ∣ + ∣ y − q ∣ = ∣ 3 − 3 ∣ + ∣ 8 − 6 ∣ = 2 |x-p|+|y-q|=|3-3|+|8-6|=2 ∣ x − p ∣ + ∣ y − q ∣ = ∣ 3 − 3 ∣ + ∣ 8 − 6 ∣ = 2 is the minimum over all pairs of p p p and q q q . Note that ( p , q ) = ( 3 , 4 ) (p,q)=(3,4) ( p , q ) = ( 3 , 4 ) is also a valid pair.
思路讲解
我们需要利用这个高位较优的这个特性,
我们设计了一个 dp,d p i , 0 dp_{i,0} d p i , 0 表示该位采用 0 , 0 0,0 0 , 0 的配置,d p i , 1 dp_{i,1} d p i , 1 表示该位采用 1 , 0 1,0 1 , 0 的配置,d p i , 2 dp_{i,2} d p i , 2 表示该位采用 0 , 1 0,1 0 , 1 的配置。
不难写出这样的转移式子:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 vector dp (40 ,vector<Diff_p_q>(3 )) ;dp[36 ][0 ]=Diff_p_q (0 ,0 ); dp[36 ][1 ]=Diff_p_q (1ll <<36 ,0 ); dp[36 ][2 ]=Diff_p_q (0 ,1ll <<36 ); for (int i=35 ;i>=0 ;--i) { dp[i]=dp[i+1 ]; dp[i][0 ]=min ({Diff_p_q (0 ,0 ),dp[i][0 ],dp[i+1 ][1 ],dp[i+1 ][2 ]}); dp[i][1 ]=min ({Diff_p_q (1ll <<i,0 ),Diff_p_q (dp[i+1 ][0 ].p+(1ll <<i),dp[i+1 ][0 ].q), Diff_p_q (dp[i+1 ][1 ].p+(1ll <<i),dp[i+1 ][1 ].q), Diff_p_q (dp[i+1 ][2 ].p+(1ll <<i),dp[i+1 ][2 ].q)}); dp[i][2 ]=min ({Diff_p_q (0 ,1ll <<i),Diff_p_q (dp[i+1 ][0 ].p,dp[i+1 ][0 ].q+(1ll <<i)), Diff_p_q (dp[i+1 ][1 ].p,dp[i+1 ][1 ].q+(1ll <<i)), Diff_p_q (dp[i+1 ][2 ].p,dp[i+1 ][2 ].q+(1ll <<i))}); }
然后你忐忑地提交了,结果 WA on 2。
你使用了对拍,或者其实你不需要使用对拍,你也知道,是因为上面的做法有一些激进,高位上比较好,那自然是比较好的,但是好分大的好和小的好 。那么上面的做法,大的好,自然是可以得到的,但是小的好,就不是那么简单的可以得到的了。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 _______________[ Testcase #1 INPUT ]_______________ 1 78 88 _______________[ Testcase #1 OUTPUT]_______________ 78 128 WA [-] FAILURE: RUNTIME ERROR Ans.diff:40 brute_diff:39 x_br:39 y_br:88
那么对拍也给了我们这个反馈,确实是这样的。
但是我们怎么样得到小的好呢?其实也简单,我们使用同样方法,整一个 dp,但是我们 dp 里存的的那个结构体的构造函数,我们传入一个指示参数,使得只要一出现大于的情况,就给他附上 d i f f ← INF diff\gets\text{INF} d i f f ← INF ,使其不够优秀而被淘汰,强制得到这个我们所谓的小的好 。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 struct Diff_p_q { ll diff,p,q; bool operator <(const Diff_p_q &o) const { return diff<o.diff; } Diff_p_q ()=default ; explicit Diff_p_q (ll p,ll q,bool no_big=false ) { if (!no_big) { diff=abs (p-X)+abs (q-Y); this ->p=p; this ->q=q; }else { if (p > X || q > Y) { diff=INF; }else { diff=abs (p-X)+abs (q-Y); } this ->p=p; this ->q=q; } } };
一样的 dp 过程。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 Diff_p_q Ans=min ({dp[0 ][0 ],dp[0 ][1 ],dp[0 ][2 ]}); { vector dp2 (40 ,vector<Diff_p_q>(3 )) ; dp2[36 ][0 ]=Diff_p_q (0 ,0 ,true ); dp2[36 ][1 ]=Diff_p_q (1ll <<36 ,0 ,true ); dp2[36 ][2 ]=Diff_p_q (0 ,1ll <<36 ,true ); for (int i=35 ;i>=0 ;--i) { dp2[i]=dp2[i+1 ]; dp2[i][0 ]=min ({Diff_p_q (0 ,0 ,true ),dp2[i][0 ],dp2[i+1 ][1 ],dp2[i+1 ][2 ]}); dp2[i][1 ]=min ({Diff_p_q (1ll <<i,0 ,true ),Diff_p_q (dp2[i+1 ][0 ].p+(1ll <<i),dp2[i+1 ][0 ].q,true ), Diff_p_q (dp2[i+1 ][1 ].p+(1ll <<i),dp2[i+1 ][1 ].q,true ), Diff_p_q (dp2[i+1 ][2 ].p+(1ll <<i),dp2[i+1 ][2 ].q,true )}); dp2[i][2 ]=min ({Diff_p_q (0 ,1ll <<i,true ),Diff_p_q (dp2[i+1 ][0 ].p,dp2[i+1 ][0 ].q+(1ll <<i),true ), Diff_p_q (dp2[i+1 ][1 ].p,dp2[i+1 ][1 ].q+(1ll <<i),true ), Diff_p_q (dp2[i+1 ][2 ].p,dp2[i+1 ][2 ].q+(1ll <<i),true )}); } Ans=min ({Ans,dp2[0 ][0 ],dp2[0 ][1 ],dp2[0 ][2 ]}); } cout<<Ans.p<<" " <<Ans.q<<"\n" ;
okay,上面我们已经把我们的思路讲的比较清楚了。
我感觉这个代码对是因为,在确定好高位以后,绝对值距离最小的数一定贴在区间边界 :要么贴上边界(低位全 0),要么贴下边界(低位全 1);中间形态永远不可能比边界更近。
不过我们的第一种 dp 由于没有引入这个 loose 和 tight,实际上计算出来的,并不是严格的 ≥ ≥ ≥ ,会有 < < < 的情况出现,所以我心里也不太踏实。欢迎 hack 我的代码。
AC代码
AC
https://codeforces.com/contest/2188/submission/360662700
源代码
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 #include <iostream> #include <numeric> #include <algorithm> #include <complex> #include <map> #include <ranges> #include <cstring> #include <string> #include <deque> #include <queue> #include <vector> #include <set> #include <cmath> #include <bitset> #include <iterator> #include <random> #include <iomanip> #include <cassert> #include <cstdio> #include <chrono> #define all(vec) vec.begin(),vec.end() #define lson(o) (o<<1) #define rson(o) (o<<1|1) #define SZ(a) ((long long) a.size()) #define debug(var) cerr << #var <<":" <<var<<"\n" ; #define cend cerr<<"\n-----------\n" #define fsp(x) fixed<<setprecision(x) using namespace std;using ll = long long ;using ull = unsigned long long ;using DB = double ;using LD = long double ;using CD = complex<double >;static constexpr ll MAXN = (ll)1e6 +10 , INF = (1ll <<61 )-1 ;static constexpr ll mod = 998244353 ; static constexpr double eps = 1e-8 ;const double pi = acos (-1.0 );ll lT; ll X,Y; struct Diff_p_q { ll diff,p,q; bool operator <(const Diff_p_q &o) const { return diff<o.diff; } Diff_p_q ()=default ; explicit Diff_p_q (ll p,ll q,bool no_big=false ) { if (!no_big) { diff=abs (p-X)+abs (q-Y); this ->p=p; this ->q=q; }else { if (p > X || q > Y) { diff=INF; }else { diff=abs (p-X)+abs (q-Y); } this ->p=p; this ->q=q; } } }; void Solve () { cin >> X >> Y; vector dp (40 ,vector<Diff_p_q>(3 )) ; dp[36 ][0 ]=Diff_p_q (0 ,0 ); dp[36 ][1 ]=Diff_p_q (1ll <<36 ,0 ); dp[36 ][2 ]=Diff_p_q (0 ,1ll <<36 ); for (int i=35 ;i>=0 ;--i) { dp[i]=dp[i+1 ]; dp[i][0 ]=min ({Diff_p_q (0 ,0 ),dp[i][0 ],dp[i+1 ][1 ],dp[i+1 ][2 ]}); dp[i][1 ]=min ({Diff_p_q (1ll <<i,0 ),Diff_p_q (dp[i+1 ][0 ].p+(1ll <<i),dp[i+1 ][0 ].q), Diff_p_q (dp[i+1 ][1 ].p+(1ll <<i),dp[i+1 ][1 ].q), Diff_p_q (dp[i+1 ][2 ].p+(1ll <<i),dp[i+1 ][2 ].q)}); dp[i][2 ]=min ({Diff_p_q (0 ,1ll <<i),Diff_p_q (dp[i+1 ][0 ].p,dp[i+1 ][0 ].q+(1ll <<i)), Diff_p_q (dp[i+1 ][1 ].p,dp[i+1 ][1 ].q+(1ll <<i)), Diff_p_q (dp[i+1 ][2 ].p,dp[i+1 ][2 ].q+(1ll <<i))}); } Diff_p_q Ans=min ({dp[0 ][0 ],dp[0 ][1 ],dp[0 ][2 ]}); { vector dp2 (40 ,vector<Diff_p_q>(3 )) ; dp2[36 ][0 ]=Diff_p_q (0 ,0 ,true ); dp2[36 ][1 ]=Diff_p_q (1ll <<36 ,0 ,true ); dp2[36 ][2 ]=Diff_p_q (0 ,1ll <<36 ,true ); for (int i=35 ;i>=0 ;--i) { dp2[i]=dp2[i+1 ]; dp2[i][0 ]=min ({Diff_p_q (0 ,0 ,true ),dp2[i][0 ],dp2[i+1 ][1 ],dp2[i+1 ][2 ]}); dp2[i][1 ]=min ({Diff_p_q (1ll <<i,0 ,true ),Diff_p_q (dp2[i+1 ][0 ].p+(1ll <<i),dp2[i+1 ][0 ].q,true ), Diff_p_q (dp2[i+1 ][1 ].p+(1ll <<i),dp2[i+1 ][1 ].q,true ), Diff_p_q (dp2[i+1 ][2 ].p+(1ll <<i),dp2[i+1 ][2 ].q,true )}); dp2[i][2 ]=min ({Diff_p_q (0 ,1ll <<i,true ),Diff_p_q (dp2[i+1 ][0 ].p,dp2[i+1 ][0 ].q+(1ll <<i),true ), Diff_p_q (dp2[i+1 ][1 ].p,dp2[i+1 ][1 ].q+(1ll <<i),true ), Diff_p_q (dp2[i+1 ][2 ].p,dp2[i+1 ][2 ].q+(1ll <<i),true )}); } Ans=min ({Ans,dp2[0 ][0 ],dp2[0 ][1 ],dp2[0 ][2 ]}); } cout<<Ans.p<<" " <<Ans.q<<"\n" ; #ifdef LOCAL if (X>100 || Y>100 ) { return ; } ll brute_diff=INF,x_br,y_br; for (int i=0 ;i<=1000 ;++i) { for (int j=0 ;j<=1000 ;++j) { if ((i&j)==0 ) { ll lans=abs (i-X)+abs (j-Y); if (lans<=brute_diff) { x_br=i; y_br=j; brute_diff=lans; } } } } if (brute_diff==Ans.diff) { return ; } cout<<"WA\n" ; debug (Ans.diff); debug (brute_diff); debug (x_br); debug (y_br); #endif } signed main () { ios::sync_with_stdio (false ); cin.tie (nullptr ); cout.tie (nullptr ); cin >> lT; while (lT--) Solve (); return 0 ; }
心路历程(WA,TLE,MLE……)