什麼毒瘤......ios
題意:給定n個d維向量,定義向量a和b的內積爲算法
求是否存在兩個向量使得它們的內積爲k的倍數,並給出任一種方案。k <= 3。ide
解:很容易想到一個暴力是n2d的。顯然咱們不能n2枚舉,因此要一次性把一個向量跟多個向量判斷。spa
先思考k = 2的狀況,顯然每一個位置和內積非0即1,這啓發咱們使用二進制。code
假如把一個內積當作一個B進制數或者一個多項式,變量是B,咱們就能發現,若是兩個向量的內積爲x,那麼這個多項式的值也是x。blog
這種狀況只要B取一個奇數就好了。理由是內積每一項非0即1,而進製爲奇數的話,每一項的xi % 2 = 1,奇偶性不變。因此最後加起來和直接加起來的奇偶性相同。it
k = 3的時候只要進製爲3a + 1就好了。因此最終咱們選擇7進制。io
而後有個很嚴峻的問題:咱們要找一個運算使之與按位乘相對應。先想到了轉成指標加法,通過一番推倒以後發現不可行。而後陷入江局......event
正解:再觀察一波內積式子,您就會發現這個實際上是矩陣乘法中的一個位置的計算式......反正我是沒發現。class
那麼令A = n × d的矩陣,B = A * AT,則Bi,j就是i和j的內積。
而後咱們只需檢驗B和全1矩陣(對角線不必定是1)是否相同便可。這有一個經典算法:隨機向量法。
隨機出來的向量哪一位不一樣,就代表在全1矩陣的哪一列中存在差別。枚舉跟這個向量匹配的向量便可。
k = 3的時候,咱們把B中每個元素都取平方,這樣1和2都會變成1。
那麼怎麼把B中的每一個元素取平方呢?
把B中某個元素的式子化開,會有:
而後就作完了......
1 #include <cstdio> 2 #include <algorithm> 3 #include <ctime> 4 #include <iostream> 5 6 const int N = 100010; 7 8 int a[N][110], now[N], C[N], D[N], E[N], MO, F[N]; 9 int n, d; 10 11 inline bool check(int i, int j) { 12 int ans = 0; 13 for(int k = 1; k <= d; k++) { 14 (ans += a[i][k] * a[j][k]) %= MO; 15 } 16 return ans; 17 } 18 19 inline void solve1() { 20 int T = 5, f = -1; 21 while((T--) && (f == -1)) { 22 int Sum = 0; 23 for(int i = 1; i <= n; i++) { 24 C[i] = rand() & 1; 25 Sum += C[i]; 26 } 27 //mul(1, n, d, C, a, D); 28 for(int i = 1; i <= d; i++) { 29 D[i] = 0; 30 for(int j = 1; j <= n; j++) { 31 D[i] += C[j] * a[j][i]; 32 D[i] &= 1; 33 } 34 } 35 //mul(1, d, n, D, aT, E); 36 for(int i = 1; i <= n; i++) { 37 E[i] = 0; 38 for(int j = 1; j <= d; j++) { 39 E[i] += D[j] * a[i][j]; 40 E[i] &= 1; 41 } 42 } 43 //mul_one(1, n, n, C, F); 44 for(int i = 1; i <= n; i++) { 45 F[i] = ((Sum - C[i]) + C[i] * now[i]) & 1; 46 if(E[i] != F[i]) { 47 f = i; 48 break; 49 } 50 } 51 } 52 if(f == -1) { 53 printf("%d %d\n", f, f); 54 return; 55 } 56 for(int i = 1; i <= n; i++) { 57 if(i == f) { 58 continue; 59 } 60 if(!check(i, f)) { 61 printf("%d %d\n", std::min(i, f), std::max(i, f)); 62 return; 63 } 64 } 65 return; 66 } 67 68 inline void solve2() { 69 int T = 5, f = -1; 70 while((T--) && (f == -1)) { 71 int Sum = 0; 72 for(int i = 1; i <= n; i++) { 73 C[i] = rand() % MO; 74 Sum += C[i]; 75 } 76 //mul(1, n, d, C, a, D); 77 for(int i = 1; i <= d; i++) { 78 for(int ii = 1; ii <= d; ii++) { 79 int pos = (i - 1) * d + ii; 80 D[pos] = 0; 81 for(int j = 1; j <= n; j++) { 82 D[pos] += C[j] * a[j][i] * a[j][ii]; 83 D[pos] %= MO; 84 } 85 } 86 } 87 //mul(1, d, n, D, aT, E); 88 for(int i = 1; i <= n; i++) { 89 E[i] = 0; 90 for(int j = 1; j <= d; j++) { 91 for(int jj = 1; jj <= d; jj++) { 92 int pos = (j - 1) * d + jj; 93 E[i] += D[pos] * a[i][j] * a[i][jj]; 94 E[i] %= MO; 95 } 96 } 97 } 98 //mul_one(1, n, n, C, F); 99 for(int i = 1; i <= n; i++) { 100 F[i] = ((Sum - C[i]) + C[i] * now[i]) % MO; 101 if(E[i] != F[i]) { 102 f = i; 103 break; 104 } 105 } 106 } 107 if(f == -1) { 108 printf("%d %d\n", f, f); 109 return; 110 } 111 for(int i = 1; i <= n; i++) { 112 if(i == f) { 113 continue; 114 } 115 if(!check(i, f)) { 116 printf("%d %d\n", std::min(i, f), std::max(i, f)); 117 break; 118 } 119 } 120 return; 121 } 122 123 int main() { 124 srand(time(0)); 125 int k, x; 126 scanf("%d%d%d", &n, &d, &k); 127 MO = k; 128 bool f = (k == 2); 129 for(int i = 1; i <= n; i++) { 130 now[i] = 0; 131 for(int j = 1; j <= d; j++) { 132 scanf("%d", &x); 133 a[i][j] = x % k; 134 now[i] += a[i][j] * a[i][j]; 135 } 136 now[i] %= k; 137 } 138 139 f ? solve1() : solve2(); 140 return 0; 141 }
題解裏還有一種神奇的解法,使用了乘法分配率,每次把一個向量和它上面全部向量的乘積加起來跟(i-1) % MO判斷。
分配一下,就是把上面向量的每一維都作前綴和,而後相乘。
這樣作其實有一點問題,就是可能有乘積爲0的檢測不出來。不過上面那種方法也彼此彼此了。