1. 程式人生 > >LOJ 2664. 「NOI2013」向量內積 解題報告

LOJ 2664. 「NOI2013」向量內積 解題報告

需要 是否 突破口 while cpp lov memset inline void

#2664. 「NOI2013」向量內積

兩個 \(d\) 維向量 \(A=[a_1, a_2 ,...,a_d]\)\(B=[b_1 ,b_2 ,...,b_d]\) 的內積為其相對應維度的權值的乘積和,即:
\[ (A,B) = \displaystyle \sum_{i=1}^d{a_ib_i} = a_1b_1 + a_2b_2 + \ldots + a_db_d \]
現有 \(n\)\(d\) 維向量 \(x_1, \ldots, x_n\),小喵喵想知道是否存在兩個向量的內積為 \(k\) 的倍數。請幫助她解決這個問題。


輸入格式

第一行包含 \(3\) 個正整數 \(n,d,k\)

,分別表示向量的個數、維數以及待檢測的倍數。

接下來 \(n\) 行每行有 \(d\) 個非負整數,其中第 \(i\) 行的第 \(j\) 個整數表示向量 \([x_i]\) 的第 \(j\) 維權值 \(x_{i,j}\)

輸出格式

包含兩個整數,用空格隔開。

如果存在兩個向量 \(x_p,x_q\) 的內積為 \(k\) 的整數倍,則輸出兩個向量的編號 \(p\)\(q\)(要求 \(p<q\))。如果存在多組這樣的向量組合,輸出其中任意一組即可。

若不存在這樣的向量組合,則輸出兩個 \(?1\)


數據範圍與提示

測試點編號 n d k \(x_i\)
\(1\)
\(2\) \(20\) \(2\) \(\le 10\)
\(2\) \(5\) \(20\) \(2\) \(\le 10\)
\(3\) \(10\) \(20\) \(3\) \(\le 10\)
\(4\) \(20\) \(20\) \(2\) \(\le 100\)
\(5\) \(50\) \(20\) \(3\) \(\le 100\)
\(6\) \(50\) \(50\) \(2\) \(\le 1000\)
\(7\) \(50\) \(50\) \(3\) \(\le 3000000\)
\(8\) \(80\) \(80\) \(2\) \(\le 2000000\)
\(9\) \(100\) \(100\)
\(3\) \(\le 3000000\)
\(10\) \(500\) \(100\) \(3\) \(\le 3000000\)
\(11\) \(1000\) \(100\) \(2\) \(\le 2000000\)
\(12\) \(1000\) \(100\) \(3\) \(\le 3000000\)
\(13\) \(10000\) \(100\) \(2\) \(< 10\)
\(14\) \(10000\) \(100\) \(3\) \(< 10\)
\(15\) \(15000\) \(100\) \(2\) \(< 10\)
\(16\) \(18000\) \(100\) \(2\) \(< 10\)
\(17\) \(20000\) \(100\) \(2\) \(< 10\)
\(18\) \(50000\) \(30\) \(3\) \(< 10\)
\(19\) \(80000\) \(30\) \(3\) \(< 10\)
\(20\) \(100000\) \(30\) \(3\) \(< 10\)

向量點乘的過程有點像一個行向量和一個列向量相乘,然後我們把原始向量排成一個矩陣\(A\),然後令\(D=A*A^T\)

那麽\(D_{i,j}\)就代表向量\(i\)和向量\(j\)做內積。

突破口在\(\bmod 2\)上。

現在矩陣所有元素在\(\bmod 2\)

我們設一個\(n\times n\)的全\(1\)矩陣\(E\),然後通過一些隨機化的方法比較\(D\)\(E\)有哪裏不相等。

我們可以隨機幾個\(1\times n\)的向量\(C\),然後判斷是否有
\[ C\times A\times A^T\equiv C\times E\pmod 2 \]
並且我們可以判斷出哪一行不相等,然後可以暴力枚舉與之匹配的另一個。

或者隨機一下原始向量的排列順序。

至於為什麽隨機次數是常數次,可以從Hash的角度感性理解

然後\(\bmod 3\)也差不多

註意到\(2^2\equiv 1\pmod 3,1^2\equiv 1\pmod 3\),我們把矩陣\(D'_{i,j}=D^2_{i,j}\)搞出來就可以了

把這個式子拆開可以發現我們需要把組成\(A\)的每一個向量搞出\(1\times d^2\)的,即\(A'_{i,(j-1)d+k}=A_{i,j}*A_{i,k}\)

然後和\(2\)是一樣的


Code:

#include <cstdio>
#include <cstring>
#include <cctype>
#include <cstdlib>
#include <algorithm>
int read()
{
    int x=0;char c=getchar();
    while(!isdigit(c)) c=getchar();
    while(isdigit(c)) x=x*10+c-'0',c=getchar();
    return x;
}
int n,d,k;
namespace beecute
{
    int yuy[20010][110],bee[110],dew[20010],c[20010];
    void work()
    {
        for(int i=1;i<=n;i++)
            for(int j=1;j<=d;j++)
                yuy[i][j]=read()&1;
        int Dew=5;
        while(Dew--)
        {
            memset(dew,0,sizeof dew);
            memset(bee,0,sizeof bee);
            for(int i=1;i<=n;i++) c[i]=rand()&1;
            for(int i=1;i<=d;i++)
                for(int j=1;j<=n;j++)
                    if(c[j])
                        bee[i]=bee[i]+yuy[j][i]&1;
            for(int i=1;i<=n;i++)
                for(int j=1;j<=d;j++)
                    dew[i]=(dew[i]+bee[j]*yuy[i][j])&1;
            for(int i=1;i<=n;i++)
                if(dew[i]!=c[i])
                {
                    for(int j=1;j<=n;j++)
                    {
                        int sum=0;
                        for(int k=1;k<=d;k++)
                            sum=(sum+yuy[i][k]*yuy[j][k])&1;
                        if(!sum)
                        {
                            if(i<j) printf("%d %d\n",i,j);
                            else printf("%d %d\n",j,i);
                            return;
                        }
                    }
                }
        }
        puts("-1");
    }
}
namespace beelovely
{
    int yuy[100010][101],bee[10010],dew[100010],c[100010];
    void work()
    {
        for(int i=1;i<=n;i++)
            for(int j=1;j<=d;j++)
                yuy[i][j]=read()%3;
        for(int i=1;i<=d;i++)
            for(int j=1;j<=d;j++)
                for(int k=1;k<=n;k++)
                    (bee[(i-1)*d+j]+=yuy[k][i]*yuy[k][j])%=3;
        int Dew=5;
        while(Dew--)
        {
            memset(dew,0,sizeof dew);
            memset(bee,0,sizeof bee);
            for(int i=1;i<=n;i++) c[i]=rand();
            for(int i=1;i<=d;i++)
                for(int k=1;k<=n;k++)
                    if(c[k])
                        bee[i]=(bee[i]+yuy[k][i]*yuy[k][j])%3;
            for(int i=1;i<=n;i++)
                for(int j=1;j<=d;j++)
                    for(int k=1;k<=d;k++)
                        dew[i]=(dew[i]+bee[(j-1)*d+k]*yuy[p[i]][j]*yuy[p[i]][k])%3;
            for(int i=1;i<=n;i++)
                if(dew[i]!=c[i])
                {
                    for(int j=1;j<=n;j++)
                    {
                        int sum=0;
                        for(int k=1;k<=d;k++)
                            sum=(sum+yuy[i][k]*yuy[j][k])&1;
                        if(!sum)
                        {
                            if(i<j) printf("%d %d\n",i,j);
                            else printf("%d %d\n",j,i);
                            return;
                        }
                    }
                }
        }
        puts("-1");
    }
}
int main()
{
    n=read(),d=read(),k=read();
    if(k==2) beecute::work();
    else beelovely::work();
    return 0;
}

2019.2.11

LOJ 2664. 「NOI2013」向量內積 解題報告