理解[bzoj 3243][Noi2013]向量内积
2016-05-21 08:02
253 查看
传送门
题目大意:给定n个d维向量,输出任意一对向量,满足他俩的内积为k的倍数。
我想了一个裸的随机算法:随机选两个向量求内积,然后。。。后10个点只过了两个。。。看到标准解法也是用的随机,一开始感觉非常不服他的随机到底比我的强在哪。。。(后来发现是我太弱)
值得一提的是,此题必须使用随机算法来确定答案。只取全1向量的做法是错误的。
一组很简单的数据就能卡掉(不信?试一试)
题解吧……先放在后面
首先!模型转换:n个d维向量构成了一个n∗d的矩阵A,现在计算A∗AT,得到的n阶方阵B中
Bi,j表示向量i与向量j的内积
好啊!非常优美,但是直接计算复杂度仍然是O(n2d)
暂时只考虑k=2
我们更关心是否存在0元素对吧
可以拿B和全一方阵C比较一下,若有不同,说明存在0元素
为了降低复杂度,我们不能对n∗n矩阵进行一一计算,所以不妨取一个1∗n的随机向量X
根据结合律,计算X∗A∗AT和X∗C,比较结果。而向量乘矩阵的复杂度是O(nd)的,比较资磁
(似乎这是判断矩阵是否相等的经典办法?)
只要有一个元素不同=>对应列上存在一个0向量,只需要暴力枚举寻找位置
没有元素不同=>X选得不好或者本来就没有
根据定理**,正确概率至少有1/2,那么重复十余次就可以得到结果了
(2014年胡泽聪的集训队论文可以参考一下)
然后问题来了:
Q1.B 中 对角线上可能存在0丫(ai∗ai=0)
A1:这个比较重要,要将干扰排除。幸运的是,只有n个内积需要计算,这样计算X∗A∗AT时候需要把这n个内积的结果减去(消除影响),然后在对角线上加一进行比较。复杂度还是O(n∗d)
Q2.为什么要随机。。直接取全一向量不行么?
A2:设D=X∗A∗AT,这里解释一下D代表的意义:
把A∗AT得到的n阶方阵B中,把第j列拿出来,把B中每一行的元素与X中元素对应相乘后相加,得到的结果就是Dj。如果X取全一向量,那么Dj就是这一列上所有元素的和。如果不全是一,那么Dj是其中一个子集的和。
看起来取全一向量非常靠谱,可是不要忘记——我们是在mod2意义下操作。如果这一列1的取值个数正好是2的倍数,那么这一个位置Dj=0。如果所有位置都是0,会被认为是无解,尽管可能有不止一对向量的内积是0。
理想的情况当然应该是先把B对2取模,然后对于转换后的矩阵进行整数系中的运算。但是……这样就不能应用乘法分配律了……
网上有一部分没有使用随机化的做法,随便找出一个,就能用开头的办法卡掉。
所以靠谱的做法是:在k=2下随机,计算结果第i列的值为从B矩阵中找出第i列一个随机子集的点积之和,还是有挺大的概率得到一个非0数字的
Q3.同样是随机,为什么裸随机不如套用矩阵进行随机?
A3:别忘了,在标准做法中,每次可以对一整列进行判定,只要有一个出错,整个就会出错,而n列的判定问题又可以在O(nd)时间内解决(随机化)。相当于是把多个内积打包在一起进行判定。
Q4.光顾着说k=2去了,k=3该怎么做?和k=2有什么区别?
A4:这一次,矩阵B中不全是1了(有可能是2),不能把它和全一矩阵比较了。
怎么办呢?把每一个内积的值平方!因为2≡−1(mod3),平方以后还是1,就转化成了全1矩阵
把内积平方拆开:(∑i=1daibi)∗(∑j=1dajbj)=∑i=1d∑j=1daiajbibj
可以看成一个d^2维向量c⃗ ,其中c(i−1)∗d+j=ai∗aj,1≤i,j≤n
b与a同理
这样就完成了问题转化,只是需要用O(d2)时间计算了
BTW
当初上uoj想找一份标程扒一下(因为太弱了,并不会),发现好多人用了一样的代码汗,而且真正用随机向量的人不多啊。
构造了反例,想Hack一下,可惜那道题是一道spj,没法Hack
于是出于业(xian)界(zhe)良(mei)心(shi),我想vfk大大发了邮件,希望加强一下数据
结果第二天发现vfk凌晨回复了邮件!并且添加了Extra Test!这种敬业精神必须要赞!!
我还想加强一下BZOJ数据。。不过TA1111爷似乎也注意到了。。比我早几天。。那么我就不把事情做绝了
题目大意:给定n个d维向量,输出任意一对向量,满足他俩的内积为k的倍数。
我想了一个裸的随机算法:随机选两个向量求内积,然后。。。后10个点只过了两个。。。看到标准解法也是用的随机,一开始感觉非常不服他的随机到底比我的强在哪。。。(后来发现是我太弱)
值得一提的是,此题必须使用随机算法来确定答案。只取全1向量的做法是错误的。
一组很简单的数据就能卡掉(不信?试一试)
meow.in 4 6 2 1 1 0 0 0 0 1 1 1 1 0 0 1 1 1 0 0 1 1 0 0 1 1 1
meow.out(样例) 1 2
题解吧……先放在后面
#include<cstdio> #include<algorithm> #include<cstring> using namespace std; typedef long long ll; #define rep(I, S, T) for (int I = S; I <= T; I ++) #define rst(ARR) memset(ARR, 0, sizeof(ARR)) int read(){ int ret = 0; char ch; do ch = getchar(); while (ch<'0' || ch>'9'); do ret = ret*10+ch-'0', ch = getchar(); while (ch>='0' && ch<='9'); return ret; } const int MAXN = 100005; const int MAXK = 105; int n, d, k; int seq[MAXN][MAXK], diag[MAXN], X[MAXN], res[MAXN], Y[MAXN]; void check(int i, int j) { int sum = 0; rep(p, 1, d) sum += seq[i][p]*seq[j][p]; sum %=k; if (sum==0 ) { if (i>j) swap(i, j); printf("%d %d\n", i, j); exit(0); } } void solve2() { rep(T, 1, 10) { int s = 0; rst(res);rst(Y); rep(i, 1, n) X[i] = rand()%k, s+=X[i]; s%=k; rep(i, 1, n) rep(j, 1, d) res[j] += seq[i][j]*X[i]; rep(i, 1, d) res[i]%=k; rep(i, 1, n) { rep(j, 1, d) Y[i] += res[j]*seq[i][j]; Y[i] += k-diag[i]*X[i]; Y[i]%=k; } rep(i, 1, n) if ((Y[i] + X[i])%k!=s) rep(j, 1, n) if (j!=i)check(i, j); } } void solve3() { rep(i, 1, n) diag[i]=(bool)diag[i]; rep(T, 1, 7) { int s = 0; rep(i, 1, d*d) res[i] = 0; rep(i, 1, n) X[i] = rand()%k, s+=X[i]; s%=k; rep(i, 1, n) { int *s = seq[i], pt = 0; rep(j, 1, d) rep(p, 1, d) res[++pt] += s[j]*s[p]*X[i]; } rep(i, 1, d*d) res[i]%=k; rep(i, 1, n) { int *sq = seq[i], pt = 0; Y[i] = 0; rep(j, 1, d) rep(p, 1, d) Y[i] += res[++pt]*sq[j]*sq[p]; Y[i] += k-diag[i]*X[i]; Y[i]%=k; if ((Y[i] + X[i])%k!=s) rep(j, 1, n) if (j!=i) check(i, j); } } } int main() { srand(223333333); scanf("%d%d%d", &n, &d, &k); rep(i, 1, n) rep(j, 1, d) seq[i][j] = read()%k; rep(i, 1, n) { rep(j, 1, d) diag[i] += seq[i][j]*seq[i][j]; diag[i]%=k; } if (k==2) solve2(); else solve3(); puts("-1 -1"); return 0; }
首先!模型转换:n个d维向量构成了一个n∗d的矩阵A,现在计算A∗AT,得到的n阶方阵B中
Bi,j表示向量i与向量j的内积
好啊!非常优美,但是直接计算复杂度仍然是O(n2d)
暂时只考虑k=2
我们更关心是否存在0元素对吧
可以拿B和全一方阵C比较一下,若有不同,说明存在0元素
为了降低复杂度,我们不能对n∗n矩阵进行一一计算,所以不妨取一个1∗n的随机向量X
根据结合律,计算X∗A∗AT和X∗C,比较结果。而向量乘矩阵的复杂度是O(nd)的,比较资磁
(似乎这是判断矩阵是否相等的经典办法?)
只要有一个元素不同=>对应列上存在一个0向量,只需要暴力枚举寻找位置
没有元素不同=>X选得不好或者本来就没有
根据定理**,正确概率至少有1/2,那么重复十余次就可以得到结果了
(2014年胡泽聪的集训队论文可以参考一下)
然后问题来了:
Q1.B 中 对角线上可能存在0丫(ai∗ai=0)
A1:这个比较重要,要将干扰排除。幸运的是,只有n个内积需要计算,这样计算X∗A∗AT时候需要把这n个内积的结果减去(消除影响),然后在对角线上加一进行比较。复杂度还是O(n∗d)
Q2.为什么要随机。。直接取全一向量不行么?
A2:设D=X∗A∗AT,这里解释一下D代表的意义:
把A∗AT得到的n阶方阵B中,把第j列拿出来,把B中每一行的元素与X中元素对应相乘后相加,得到的结果就是Dj。如果X取全一向量,那么Dj就是这一列上所有元素的和。如果不全是一,那么Dj是其中一个子集的和。
看起来取全一向量非常靠谱,可是不要忘记——我们是在mod2意义下操作。如果这一列1的取值个数正好是2的倍数,那么这一个位置Dj=0。如果所有位置都是0,会被认为是无解,尽管可能有不止一对向量的内积是0。
理想的情况当然应该是先把B对2取模,然后对于转换后的矩阵进行整数系中的运算。但是……这样就不能应用乘法分配律了……
网上有一部分没有使用随机化的做法,随便找出一个,就能用开头的办法卡掉。
所以靠谱的做法是:在k=2下随机,计算结果第i列的值为从B矩阵中找出第i列一个随机子集的点积之和,还是有挺大的概率得到一个非0数字的
Q3.同样是随机,为什么裸随机不如套用矩阵进行随机?
A3:别忘了,在标准做法中,每次可以对一整列进行判定,只要有一个出错,整个就会出错,而n列的判定问题又可以在O(nd)时间内解决(随机化)。相当于是把多个内积打包在一起进行判定。
Q4.光顾着说k=2去了,k=3该怎么做?和k=2有什么区别?
A4:这一次,矩阵B中不全是1了(有可能是2),不能把它和全一矩阵比较了。
怎么办呢?把每一个内积的值平方!因为2≡−1(mod3),平方以后还是1,就转化成了全1矩阵
把内积平方拆开:(∑i=1daibi)∗(∑j=1dajbj)=∑i=1d∑j=1daiajbibj
可以看成一个d^2维向量c⃗ ,其中c(i−1)∗d+j=ai∗aj,1≤i,j≤n
b与a同理
这样就完成了问题转化,只是需要用O(d2)时间计算了
BTW
当初上uoj想找一份标程扒一下(因为太弱了,并不会),发现好多人用了一样的代码汗,而且真正用随机向量的人不多啊。
构造了反例,想Hack一下,可惜那道题是一道spj,没法Hack
于是出于业(xian)界(zhe)良(mei)心(shi),我想vfk大大发了邮件,希望加强一下数据
结果第二天发现vfk凌晨回复了邮件!并且添加了Extra Test!这种敬业精神必须要赞!!
我还想加强一下BZOJ数据。。不过TA1111爷似乎也注意到了。。比我早几天。。那么我就不把事情做绝了
相关文章推荐
- 我对特征值与特征向量的理解
- <deep learning>读书笔记(一)特征分解与坐标变换
- 马尔科夫状态转移矩阵的一个性质,以及一个直观的图形证明
- 傅里叶变换和线性空间
- 【转】理解矩阵(三)--看了这些才发觉,读大学的作用就是要用的时候不用再买书了 ̄□ ̄||
- [转]理解矩阵(一)--个人觉得是最好的,线性代数都弱爆了
- PCA以及降维的思想
- Stanford 机器学习课程cs229 数学推导知识
- 理解矩阵
- 基于特征值的斐波那契数列求解
- 从多元方程组到矩阵计算的乘法法则
- 快速排序
- 基于 intel MKL 的对称矩阵特征值求解器
- 快速排序(随机化)
- 三阶矩阵的特征值一般求解
- 线性代数复习一——线性代数中的线性方程组
- 线性代数复习四——矩阵的维数和秩
- 线性代数复习六——向量空间
- 特征值 特征向量, 你怎么看?
- 理解矩阵