洛谷P1224 向量內積

什麼毒瘤......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 }
AC代碼

題解裏還有一種神奇的解法,使用了乘法分配率,每次把一個向量和它上面全部向量的乘積加起來跟(i-1) % MO判斷。

分配一下,就是把上面向量的每一維都作前綴和,而後相乘。

這樣作其實有一點問題,就是可能有乘積爲0的檢測不出來。不過上面那種方法也彼此彼此了。

相關文章
相關標籤/搜索