[BZOJ]3243 向量内积(Noi2016)

  小C做了之后很有感觉的题目之一,但因为姿势不对调了很久。

Description

  两个d 维向量A=[a1,a2,...,ad]与B=[b1,b2,...,bd]的内积为其相对应维度的权值的乘积和,即:

      

  现有 n 个d 维向量x1,...,xn ,小喵喵想知道是否存在两个向量的内积为k的倍数。请帮助她解决这个问题。

Input

  第一行包含3个正整数n,d,k,分别表示向量的个数,维数以及待检测的倍数。接下来n行每行有d个非负整数,其中第i行的第j个整数表示向量xi的第j维权值xi,j。

  N<=100000,D<=30,K<=3,Xi,j<10

Output

  包含两个整数,用空格隔开。如果存在两个向量xp,xq的内积为k的整数倍,则输出两个向量的编号p与q(要求p<q)。如果存在多组这样的向量组合,输出其中任意一组即可。若不存在这样的向量组合,则输出两个-1。

Sample Input

  2 20 2

  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

  1 2

Hint

  

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=0,f=1;char c=getchar();
    while (c<‘0‘ || c>‘9‘) {if(c==‘-‘)f=-1; c=getchar();}
    while (c>=‘0‘ && c<=‘9‘) {n=n*10+c-‘0‘; 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=0;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=0;i<c.h;++i)
        for (j=0;j<c.l;c.ar[i][j++]%=mod)
            for (c.ar[i][j]=0,k=0;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=0;i<c.h;++i)
        for (j=0;j<c.l;c.ar[i][j++]%=mod)
            for (c.ar[i][j]=0,kl=0;kl<a.l;++kl)
                for (kr=0;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=0;i<c.h;++i)
    {
        register int x=i/a.h,y=i%a.h;
        for (j=0;j<c.l;c.ar[i][j++]%=mod)
            for (c.ar[i][j]=0,k=0;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=0;i<n;++i)
        for (j=0;j<m;++j) a.ar[i][j]=rand()%mod;
    return a;
}

int main()
{
    srand(23353);
    register int i,j,k,o;
    n=read(); m=read(); mod=read();
    a=newmatr(n,m); b=newmatr(m,n);
    for (i=0;i<n;++i)
        for (j=0;j<m;++j) a.ar[i][j]=b.ar[j][i]=read()%mod;
    for (i=0;i<n;g[i++]%=mod)
        for (j=0;j<m;++j) g[i]+=a.ar[i][j]*a.ar[i][j];
    if (mod==2)
    {
        for (o=1;o<=5;++o)
        {
            r=ranmat(n,1);
            h=a*(b*r);
            for (i=sum=0;i<n;++i) sum^=r.ar[i][0];
            for (i=0;i<n;++i) if (h.ar[i][0]!=(sum^(r.ar[i][0]&&!g[i]))) break;
            if (i<n) break;
        }
    }
    else if (mod==3)
    {
        for (i=0;i<n;++i) g[i]=g[i]*g[i]%mod;
        for (o=1;o<=5;++o)
        {
            r=ranmat(n,1);
            h=a&(b|r);
            for (i=sum=0;i<n;++i) sum+=r.ar[i][0];
            for (i=0;i<n;++i) if (h.ar[i][0]!=(sum-(1-g[i])*r.ar[i][0])%mod) break;
            if (i<n) break;
        }
    }
    if (o>5) return 0*printf("-1 -1");
    for (j=0;j<n;++j)
    {
        if (i==j) continue;
        for (sum=k=0;k<m;++k) sum+=a.ar[i][k]*a.ar[j][k];
        if (!(sum%mod)) return 0*printf("%d %d",min(i+1,j+1),max(i+1,j+1));
    }
}

Last Word

  反正依着小C这属性(才不是抖M),能让他发博客吐槽的题目大概又是什么虐了他千百遍的丧题吧。(其实是因为小C智障老是打挂题)

  一开始非常智障地跑去建n*(d^2)的矩阵,然后,然后就有了以上一段画风崩坏的代码……

  由于用了结构体矩阵来计算,代码似乎跑得出奇地慢,洛谷和BZOJ上都过了,自家的OJ(未开O2)死活过不去……

  所以不建议读者按矩阵计算,直接开数组即可,还有n*1的矩阵只要开成一维矩阵即可,这样代码效率可以有巨大的提升。

  小C的代码效率较慢的锅都是因为开了二维数组,似乎 new 和 return结构体 在这道题效率还行?

  总之,小C觉得这还算是一道挺有想法的题啦。其主要思想就是利用矩阵乘法的结合律来改变计算顺序以降低时间复杂度,还有就是在判断两个矩阵是否相等时用了一些小技巧,至于2^2≡1(mod 3)什么的就是脑洞问题了吧!(QAQ觉得脑洞平了)

时间: 2024-10-26 12:02:08

[BZOJ]3243 向量内积(Noi2016)的相关文章

3243: [Noi2013]向量内积 - BZOJ

Description 两个d 维向量A=[a1,a2,...,ad]与B=[b1,b2,...,bd]的内积为其相对应维度的权值的乘积和,即: 现有 n 个d 维向量x1,...,xn ,小喵喵想知道是否存在两个向量的内积为k的倍数.请帮助她解决这个问题Input 第一行包含3个正整数n,d,k,分别表示向量的个数,维数以及待检测的倍数.接下来n行每行有d个非负整数,其中第i行的第j个整数表示向量xi的第j维权值xi,j.Output 包含两个整数,用空格隔开.如果存在两个向量xp,xq的内积

[Noi2013]向量内积

来自FallDream的博客,未经允许,请勿转载,谢谢. 两个d 维向量A=[a1,a2,...,ad]与B=[b1,b2,...,bd]的内积为其相对应维度的权值的乘积和,即: 现有 n 个d 维向量x1,...,xn ,小喵喵想知道是否存在两个向量的内积为k的倍数.请帮助她解决这个问题 k=2时 n<=20000 d<=100  k=3时n<=1000,d<=100 或者n<=100000 d<=30 把两个向量内积看作矩阵一个1*d的矩阵和d*1的矩阵相乘 那么k

编程题-最小向量内积-(1)

有两个向量v1=(x1,x2,...,xn)和v2=(y1,y2,...,yn),允许任意交换v1和v2各自的分量的顺序,计算v1和v2的内积x1y1+x2y2+...+xnyn的最小值 样例: 输入: n=3 v1=(1,3,?5) v2=(?2,4,1) 输出: -25 ( 令v1=(?5,1,3),v2=(4,1,?2) ) 首先我第一感觉就是,要保证最小, 1.如果在有正有负的情况下,那么有最大的正数乘以最小的负数, 2.如果都是正数,用最大的正数乘以最小的正数, 3.如果都是负数,用最

向量内积&amp;外积

一.向量的内积 1.1向量内积的定义 概括地说,向量的内积(点乘/点积/数量积)就是对两个向量执行点乘运算,即对这两个向量对应位一一相乘之后求和的操作,如下所示,对于向量a和向量b:                  a和b的点积公式为: 这里要求一维向量a和向量b的行列数相同.注意:点乘的结果是一个标量(数量而不是向量). 定义:两个向量a与b的内积为 a·b = |a||b|cos∠(a, b),特别地,0·a =a·0 = 0:若a,b是非零向量,则a与b正交的充要条件是a·b = 0.

BZOJ3243 [Noi2013]向量内积/UOJ121

本文版权归ljh2000和博客园共有,欢迎转载,但须保留此声明,并给出原文链接,谢谢合作. 本文作者:ljh2000 作者博客:http://www.cnblogs.com/ljh2000-jump/转载请注明出处,侵权必究,保留最终解释权! Description 两个d 维向量A=[a1,a2,...,ad]与B=[b1,b2,...,bd]的内积为其相对应维度的权值的乘积和,即: 现有 n 个d 维向量x1,...,xn ,小喵喵想知道是否存在两个向量的内积为k的倍数.请帮助她解决这个问题

【uoj121】 NOI2013—向量内积

http://uoj.ac/problem/121 (题目链接) 题意 给出${n}$个${d}$维向量,问是否有两个不同的向量的内积是${k}$的倍数. Solution 又卡了一上午常数,我弃了T_T. 右转题解→_→:llg 代码 // uoj121 #include<algorithm> #include<iostream> #include<cstdlib> #include<cstring> #include<cstdio> #inc

BZOJ 2299 向量(裴蜀定理)

题意:给你一对数a,b,你可以任意使用(a,b), (a,-b), (-a,b), (-a,-b), (b,a), (b,-a), (-b,a), (-b,-a)这些向量,问你能不能拼出另一个向量(x,y). 实际上前四个向量能拼出(ma,nb)(m%2=n%2).后四个向量拼出(xb,ya)(x%2=y%2). 这样可以枚举这四个未知数在模二意义下的解.这两个向量相加为(ma+xb,nb+ya). 对于ma+xb=X.根据系数的奇偶性,如果有系数为奇数,可使得等式两边都减去一个数使得系数都为偶

BZOJ 4311: 向量( 按时间分治 + 线段树 )

离线, 然后按时间分治, 每个向量都有出现时间[l, r], 直接插入时间线段树(一个向量只会影响O(logN)数量级的线段树节点). 在线段树每个节点弄出凸壳然后二分. 时间复杂度O(Nlog^2N) --------------------------------------------------------------------------- #include<cstdio> #include<cctype> #include<cstring> #includ

BZOJ 4311 向量

shallot+向量集 混合版? 首先我们考虑每个向量的存在时间为[L,R] 那么我们知道任意一个区间在线段树上最多被分解成logn个区间 那么我们可以像shallot一样进行区间覆盖 注意到本题的查询是在凸壳上完成的,而凸壳不像shallot的线性基一样有固定的时间复杂度 但是本题的查询是可分离的,那么我们不需要将向量下传,只需要在线段树的每一层做凸壳即可 查询时每走一层对该层三分取最优解,建造凸壳和三分方法同向量集 QAQ 上午因为排序不小心写反了符号调了好久 QAQ 时间复杂度O(nlog