[BZOJ]3243 向量内积(Noi2013)
小C做了之后很有感觉的题目之一,但因为姿势不对调了很久。
Description
两个d 维向量A=[a1,a2,...,ad]与B=[b1,b2,...,bd]的内积为其相对应维度的权值的乘积和,即:
.jpg)
现有 n 个d 维向量x1,...,xn ,小喵喵想知道是否存在两个向量的内积为k的倍数。请帮助她解决这个问题。
Input
Output
Sample Input
0 0 1 1 1 1 1 0 1 1 1 0 1 0 0 0 1 1 1 1
1 0 1 0 1 0 1 1 1 1 0 1 1 1 0 1 1 0 1 0
Sample Output
Hint
N<=100000,D<=30,K<=3,Xi,j<10。

Solution
看见题目后基本能想到的要点:
①k似乎很小?k=2时答案只可能是0或1,k=3则是0、1和2?
②向量内积的求和式其实就是矩阵乘法的计算式,两个向量的内积可以看做是1*d和d*1的矩阵相乘。
继续深入:
①可以将题目表示为n*d和d*n的矩阵相乘,得到的n*n的矩阵就是n个向量两两之间的内积,判断n*n矩阵内是否有等于0的元素即可;
②直接计算和判断明显复杂度爆表,我们考虑将计算和判断部分进行优化:
当k=2时,n*n的矩阵只有一种情况不是我们想要的,那就是除了对角线上的元素,其他都为1——设这样的矩阵为D;
设原来的两个矩阵分别为A、B,相乘得到矩阵C;
这样我们只需判断C和D是否相等即可,我们当然不会直接判断,这里需要用到一个小技巧。
我们考虑随机生成一个n*1的矩阵T来与C、D相乘,判断C*T和D*T是否相等即可。
随机次数3~5次基本不会出错,这样我们便将判断成功降到了O(n)。
至于计算部分,我们只要按A*(B*T)的顺序就可以O(nd)得到C*T;
由于矩阵D是确定的,主对角线O(nd)计算,O(n)再推一遍可以得到D*T。
剩下的事情就是找出不相等的一位x,相当于找到了答案的一半(x一定是答案),O(nd)找另一半即可。
当k=3时,我们可以脑补一个2^2≡1(mod 3),于是考虑将矩阵C、D的每个元素平方(注意不是将矩阵平方!)。
这样矩阵D的每个除了主对角线上的元素便都是1了,像k=2那样判断C和D是否相等即可。
至于怎么计算矩阵C呢?我们发现

所以可以将矩阵A和B分别扩展成n*(d^2)和(d^2)*n的矩阵;
即将d维向量扩展成一个d^2维的向量,新向量每一维都是旧向量某两维的乘积。
有了这些,步骤基本就和k=2时的情况一样了。
时间复杂度:O(nd^2)
你会发现一个细节,数据中有n=10000,d=100,k=3的情况,n*(d^2)的空间完全不够开。
当然没有叫你真的开一个n*(d^2)的矩阵啊(坏笑)!你会发现涉及到矩阵乘法的操作只有在计算C*T的时候,也就是说只有4次,只要把k=3的那两次特殊抠出来处理即可,具体实现可以看小C的代码。
#include <cstdio>
#include <algorithm>
#include <cstdlib>
#define MN 100005
#define MM 105
using namespace std;
struct matrix{int **ar; int h,l;}a,b,r,h;
int g[MN],f[MM],mod,n,m,sum; inline int read()
{
int n=,f=;char c=getchar();
while (c<'' || c>'') {if(c=='-')f=-; c=getchar();}
while (c>='' && c<='') {n=n*+c-''; c=getchar();}
return n*f;
} matrix newmatr(int n,int m)
{
matrix a;
a.h=n; a.l=m;
a.ar=new int*[n];
for (register int i=;i<n;++i) a.ar[i]=new int[m];
return a;
} matrix operator*(const matrix& a,const matrix& b)
{
matrix c=newmatr(a.h,b.l);
register int i,j,k;
for (i=;i<c.h;++i)
for (j=;j<c.l;c.ar[i][j++]%=mod)
for (c.ar[i][j]=,k=;k<a.l;++k) c.ar[i][j]+=a.ar[i][k]*b.ar[k][j];
return c;
} matrix operator&(const matrix& a,const matrix& b)
{
matrix c=newmatr(a.h,b.l);
register int i,j,kl,kr;
for (i=;i<c.h;++i)
for (j=;j<c.l;c.ar[i][j++]%=mod)
for (c.ar[i][j]=,kl=;kl<a.l;++kl)
for (kr=;kr<a.l;++kr) c.ar[i][j]+=a.ar[i][kl]*a.ar[i][kr]*b.ar[kl*a.l+kr][j];
return c;
} matrix operator|(const matrix& a,const matrix& b)
{
matrix c=newmatr(a.h*a.h,b.l);
register int i,j,k;
for (i=;i<c.h;++i)
{
register int x=i/a.h,y=i%a.h;
for (j=;j<c.l;c.ar[i][j++]%=mod)
for (c.ar[i][j]=,k=;k<a.l;++k) c.ar[i][j]+=a.ar[x][k]*a.ar[y][k]*b.ar[k][j];
}
return c;
} matrix ranmat(int n,int m)
{
matrix a=newmatr(n,m);
register int i,j;
for (i=;i<n;++i)
for (j=;j<m;++j) a.ar[i][j]=rand()%mod;
return a;
} int main()
{
srand();
register int i,j,k,o;
n=read(); m=read(); mod=read();
a=newmatr(n,m); b=newmatr(m,n);
for (i=;i<n;++i)
for (j=;j<m;++j) a.ar[i][j]=b.ar[j][i]=read()%mod;
for (i=;i<n;g[i++]%=mod)
for (j=;j<m;++j) g[i]+=a.ar[i][j]*a.ar[i][j];
if (mod==)
{
for (o=;o<=;++o)
{
r=ranmat(n,);
h=a*(b*r);
for (i=sum=;i<n;++i) sum^=r.ar[i][];
for (i=;i<n;++i) if (h.ar[i][]!=(sum^(r.ar[i][]&&!g[i]))) break;
if (i<n) break;
}
}
else if (mod==)
{
for (i=;i<n;++i) g[i]=g[i]*g[i]%mod;
for (o=;o<=;++o)
{
r=ranmat(n,);
h=a&(b|r);
for (i=sum=;i<n;++i) sum+=r.ar[i][];
for (i=;i<n;++i) if (h.ar[i][]!=(sum-(-g[i])*r.ar[i][])%mod) break;
if (i<n) break;
}
}
if (o>) return *printf("-1 -1");
for (j=;j<n;++j)
{
if (i==j) continue;
for (sum=k=;k<m;++k) sum+=a.ar[i][k]*a.ar[j][k];
if (!(sum%mod)) return *printf("%d %d",min(i+,j+),max(i+,j+));
}
}
Last Word
反正依着小C这属性(才不是抖M),能让他发博客吐槽的题目大概又是什么虐了他千百遍的丧题吧。(其实是因为小C智障老是打挂题)
一开始非常智障地跑去建n*(d^2)的矩阵,然后,然后就有了以上一段画风崩坏的代码……
由于用了结构体矩阵来计算,代码似乎跑得出奇地慢,洛谷和BZOJ上都过了,自家的OJ(未开O2)死活过不去……
所以不建议读者按矩阵计算,直接开数组即可,还有n*1的矩阵只要开成一维矩阵即可,这样代码效率可以有巨大的提升。
小C的代码效率较慢的锅都是因为开了二维数组,似乎 new 和 return结构体 在这道题效率还行?
总之,小C觉得这还算是一道挺有想法的题啦。其主要思想就是利用矩阵乘法的结合律来改变计算顺序以降低时间复杂度,还有就是在判断两个矩阵是否相等时用了一些小技巧,至于2^2≡1(mod 3)什么的就是脑洞问题了吧!(QAQ觉得脑洞平了)
[BZOJ]3243 向量内积(Noi2013)的更多相关文章
- BZOJ 3243 向量内积
Description 两个\(d\)维向量\(A=[a_{1},a_{2},...,a_{d}]\)与\(B=[b_{1},b_{2},...,b_{d}]\)的内积为其相对应维度的权值的乘积和,即 ...
- 【BZOJ3243】【NOI2013】向量内积(矩阵,数论)
[BZOJ3243][NOI2013]向量内积(矩阵,数论) 题面 BZOJ 题解 这题好神仙. 首先\(60\)分直接是送的.加点随机之类的可以多得点分. 考虑正解. 我们先考虑一下暴力. 我们把\ ...
- [Noi2013]向量内积
来自FallDream的博客,未经允许,请勿转载,谢谢. 两个d 维向量A=[a1,a2,...,ad]与B=[b1,b2,...,bd]的内积为其相对应维度的权值的乘积和,即: $\sum_{i=1 ...
- LOJ 2664. 「NOI2013」向量内积 解题报告
#2664. 「NOI2013」向量内积 两个 \(d\) 维向量 \(A=[a_1, a_2 ,...,a_d]\) 与 \(B=[b_1 ,b_2 ,...,b_d]\) 的内积为其相对应维度的权 ...
- 【fake题解】[NOI2013]向量内积
[fake题解][NOI2013]向量内积 做法1 大暴力.哪里不会T哪里. 做法2 所有数都%=k不影响结果.(废话 k的取值只有2和3,所以肯定是要分类讨论的.k=2肯定简单些啦. k=2 出现的 ...
- P1224 [NOI2013]向量内积
传送门 发现这个内积和矩乘有点像,考虑构造一个 $n$ 行 $m$ 列的矩阵 $A$,每一行都是一个题目给定的 $m$ 维向量 设 $B=AA^T$ ,其中 $A^T$ 为 $A$ 的转置矩阵,那么对 ...
- luogu P1224 [NOI2013]向量内积
传送门 挺有意思的一道题 暴力60就是枚举每个向量暴力check,随机选向量就能多骗一些分 然后两个向量内积要模\(k\)为\(0\),那么如果全部不为\(0\)就不合法.先考虑\(k=2\),对于向 ...
- 【BZOJ-3243】向量内积 随机化 + 矩阵
3243: [Noi2013]向量内积 Time Limit: 10 Sec Memory Limit: 256 MBSec Special JudgeSubmit: 1249 Solved: ...
- 3243: [Noi2013]向量内积 - BZOJ
Description 两个d 维向量A=[a1,a2,...,ad]与B=[b1,b2,...,bd]的内积为其相对应维度的权值的乘积和,即: 现有 n 个d 维向量x1,...,xn ,小喵喵想知 ...
随机推荐
- class AClass<E extends Comparable>与class AClass<E extends Comaprable<E>>有什么区别?
new ArrayList<>()与new ArrayList()一样 都是为了做限定用的 如果不了解你可以看API 这个Comparable里面有一个方法compareTo(T o) 如 ...
- NOIP2016 天天爱跑步 80分暴力
https://www.luogu.org/problem/show?pid=1600 题目描述 小c同学认为跑步非常有趣,于是决定制作一款叫做<天天爱跑步>的游戏.«天天爱跑步»是一个养 ...
- 03-移动端开发教程-CSS3新特性(下)
1. CSS3动画 1.1 过渡的缺点 transition的优点在于简单易用,但是它有几个很大的局限. transition需要事件触发,所以没法在网页加载时自动发生. transition是一次性 ...
- IE浏览器支持响应式网站设计
目前响应式网站设计比较流行, 下面是摘自百度百科有关响应式设计的定义. 响应式网站设计是一种网络页面设计布局,其理念是:集中创建页面的图片排版大小,可以智能地根据用户行为以及使用的设备环境进行相对应的 ...
- css3动画transition详解
一.transition-property 语法: transition-property : none | all | [ <IDENT> ] [ ',' <IDENT> ] ...
- JAVA_SE基础——22.面向对象的概念
我写博客是为了提升自己和为了进入黑马程序员学习,还有分享些自己的心得给大家,希望能帮助大家学习JAVA. 我是自学的,如果写的有错误或者能更好的修改的请提出. 在这里我先引用下<think in ...
- Angular 学习笔记 ( CDK - Accessibility )
@angular/ckd 是 ng 对于 ui 组建的基础架构. 是由 material 团队开发与维护的, 之所以会有 cdk 看样子是因为在开发 material 的时候随便抽象一个层次出来给大家 ...
- LXC学习实践(2)安装LXC
1.准备工作: yum install gcc yum install libcap-devel yum install libcgroup 2.安装LXC 下载源代码:sourceforge.net ...
- Linux实战案例(3)创建和删除用户
建用户: adduser phpq //新建phpq用户passwd phpq //给php ...
- express学习(二)—— Post()类型和中间件
1.数据:GET.POST 2.中间件:使用.写.链式操作 GET-无需中间件 req.query POST-需要"body-parser" server.use(bodyPars ...