zoukankan      html  css  js  c++  java
  • [NOI 2013] 向量内积

    题目

    传送门

    解法

    观察数据,我们发现 (k) 只有 (2,3) 两种情况,这提醒我们分类讨论。

    ( ext{case}_1:k=2)

    不妨将两两向量内积化成一个矩阵:设 (n)(d) 维行向量组成的矩阵为 (A),那么这个矩阵为 (Y=AA^T)

    对于 (k=2),我们只需判断 (Y) 是否等于 (E)(E) 是全 (1) 矩阵)。

    用类似 这道题 的方法来判断即可。在判断时,可以用 (mathcal O(nd)) 内找出哪个行向量 (pos)(0)。然后我们再枚举其它向量,暴力计算它与 (pos) 的内积即可。

    总时间复杂度 (mathcal O(nd))

    ( ext{case}_2:k=3)

    因为 (Y) 最终可能有 (0,1,2) 三种值,上面的方法就行不通了。

    然后有个很妙的转化:设 (Z_{i,j}=Y_{i,j}^2pmod 3)。这样就又变成了 (0,1) 两种值。

    问题在于,(Z)(Y) 单项的平方,并不能直接矩乘获得,所以尝试推一推式子((alpha) 是随机的向量):

    [(Zalpha)_i=sum_{j=1}^nZ_{i,j}alpha_j ]

    [=sum_{j=1}^nY_{i,j}^2alpha_j ]

    [=sum_{j=1}^nalpha_jleft(sum_{k=1}^d A_{i,k}A^T_{k,j} ight)^2 ]

    这时可以做到 (mathcal O(n^2d)) 的复杂度,但它和暴力复杂度是一样的。可以尝试抛开与 (i) 无关的项然后进行预处理。

    [=sum_{j=1}^nalpha_jleft(sum_{k_1=1}^d A_{i,k_1}A_{j,k_1} ight)left(sum_{k_2=1}^d A_{i,k_2}A_{j,k_2} ight) ]

    [=sum_{k_1=1}^d sum_{k_2=1}^d A_{i,k_1}A_{i,k_2}sum_{j=1}^n alpha_jA_{j,k_1}A_{j,k_2} ]

    后面一坨可以 (mathcal O(nd^2)) 预处理成函数 (g(k_1,k_2))。因为还要枚举 (i),来判断 ((Zalpha)_i)((Ealpha)_i) 是否相等,也是 (mathcal O(nd^2)) 的。

    我随机了 (5) 次,类似 这道题 的方法,得出错误概率为 (frac{1}{2^5})(感觉好奇怪)。

    代码

    #include <cstdio>
    
    #define print(x,y) write(x),putchar(y) 
    
    template <class T> inline T read(const T sample) {
        T x=0; int f=1; char s;
        while((s=getchar())>'9'||s<'0') if(s=='-') f=-1;
        while(s>='0'&&s<='9') x=(x<<1)+(x<<3)+(s^48),s=getchar();
        return x*f;
    }
    template <class T> inline void write(const T x) {
        if(x<0) return (void) (putchar('-'),write(-x));
        if(x>9) write(x/10);
        putchar(x%10^48);
    }
    
    #include <cstdlib>
    #include <iostream>
    using namespace std;
    
    const int maxn=1e5+5,maxd=105;
    
    int n,d,k,v[maxn][maxd],sum;
    int a[maxd],r[maxn],pos;
    int g[maxd][maxd];
    bool flag;
    
    void calc_2() {
    	for(int i=1;i<=d;++i) a[i]=0;
    	for(int i=1;i<=d;++i)
    		for(int j=1;j<=n;++j)
    			a[i]=(a[i]+v[j][i]*r[j])%k;
    	for(int i=1;i<=n;++i) {
    		int tmp=0;
    		for(int j=1;j<=d;++j)
    			tmp=(tmp+a[j]*v[i][j])%k;
    		if(tmp!=sum) {
    			flag=1,pos=i;
    			break;
    		}
    	}
    }
    
    void calc_3() {
    	for(int k1=1;k1<=d;++k1)
    		for(int k2=1;k2<=d;++k2) {
    			g[k1][k2]=0;
    			for(int j=1;j<=n;++j)
    				g[k1][k2]=(g[k1][k2]+
    							v[j][k1]*v[j][k2]*r[j])%k;
    		}
    	for(int i=1;i<=n;++i) {
    		int tmp=0;
    		for(int k1=1;k1<=d;++k1)
    			for(int k2=1;k2<=d;++k2)
    				tmp+=v[i][k1]*v[i][k2]*g[k1][k2];
    		if(tmp%k!=sum) {
    			flag=1,pos=i;
    			break;
    		}
    	}
    }
    
    int main() {
    	srand(12242013);
    	n=read(9),d=read(9),k=read(9);
    	for(int i=1;i<=n;++i)
    		for(int j=1;j<=d;++j)
    			v[i][j]=read(9)%k;
    	for(int T=1;T<=5;++T) {
    		sum=0;
    		for(int i=1;i<=n;++i)
    			r[i]=rand()%k,sum=(sum+r[i])%k;
    		if(k==2) calc_2();
    		else calc_3();
    		if(flag) break;
    	}
    	if(!flag) puts("-1 -1");
    	else {
    		for(int i=1;i<=n;++i)
    			if(i^pos) {
    				int tmp=0;
    				for(int j=1;j<=d;++j)
    					tmp+=v[pos][j]*v[i][j];
    				if(tmp%k==0) {
    					printf("%d %d
    ",min(pos,i),max(pos,i));
    					break;
    				}
    			}
    	}
    	return 0;
    }
    
  • 相关阅读:
    Intent属性详解
    LIBGDX游戏引擎平台介绍与搭建
    android教程之intent对象
    android教程之日期时间控件DatePicker/TimePicker
    DotNet Core 3.1 EF Core 数据库迁移(Migration)
    微服务介绍
    Asp.Net Core 认证授权:Cookie-based
    IdentityServer4 实现自定义 GrantType 授权模式
    SqlServer配置主从复制
    在【Stimulsoft-Reports-Net-2016.1】中使用DataSet做数据源新建报表
  • 原文地址:https://www.cnblogs.com/AWhiteWall/p/15037038.html
Copyright © 2011-2022 走看看