题目分析
注意到本题只求一种方案,所以应该有什么不精确但快速的方法判定可行。
对于K=2的情况:
考虑这样一种判定方法,每次求出当前向量(设为第i个向量)与前面所有向量的内积之和(模2意义下),假如获得的这个值与模2意义下的i-1不相等,说明它与前面每个向量的内积中出现了0,然后暴力从前面的向量里找即可。
求前缀和可以很快,因为事先维护一下每一维前面所有向量的前缀和即可,复杂度
O(nd)
O
(
n
d
)
。
唯一的问题是这样的判定有可能错过可行解(因为是模2意义下的),为了防止变♂态出题人故意卡,所以把所有向量随机打乱一下即可。
对于K=3的情况:
由于内积可能出现1和2,所以似乎判定没那么方便了。注意到
1∗1≡2∗2(mod3)
1
∗
1
≡
2
∗
2
(
mod
3
)
,所以我们可以把求它与前面所有向量的内积之和变成它与前面所有向量的内积的平方和。
a与b向量的平方和:
(a⋅b)2=∑di=1∑dj=1aiajbibj
(
a
⋅
b
)
2
=
∑
i
=
1
d
∑
j
=
1
d
a
i
a
j
b
i
b
j
所以我们维护sum[i][j]
表示在以上i值和j值的前提下的前缀和,这样也能做到
O(nd2)
O
(
n
d
2
)
判定。
至此完美解决本题。
然后记得要输出的解要小的在前,大的在后,不然UOJ上会WA。
代码
#include<bits/stdc++.h>
using namespace std;
#define RI register int
typedef long long LL;
LL read() {
LL q=0;char ch=' ';
while(ch<'0'||ch>'9') ch=getchar();
while(ch>='0'&&ch<='9') q=q*10+(LL)(ch-'0'),ch=getchar();
return q;
}
int n,d,K;
const int N=100001;
int a[N][101],s[101],sum[101][101],p[N];
#define mii(a,b) (a<b?a:b)
#define mxx(a,b) (a>b?a:b)
int niconi(int x) {
int re=0;
for(RI i=1;i<=d;++i) re+=s[i]*a[x][i],s[i]+=a[x][i];
return re&1;
}
void work2() {
for(RI i=1;i<=n;++i)
if(niconi(p[i])!=((i-1)&1)) {
for(RI j=1;j<i;++j) {
int js=0;
for(RI k=1;k<=d;++k) js+=a[p[i]][k]*a[p[j]][k];
if(!(js&1))
{printf("%d %d\n",mii(p[i],p[j]),mxx(p[i],p[j]));return;}
}
}
puts("-1 -1");
}
int orz(int x) {
int re=0;
for(RI i=1;i<=d;++i)
for(RI j=1;j<=d;++j)
re+=sum[i][j]*a[x][i]*a[x][j],sum[i][j]+=a[x][i]*a[x][j];
return re%3;
}
void work3() {
for(RI i=1;i<=n;++i)
if(orz(p[i])!=(i-1)%3) {
for(RI j=1;j<i;++j) {
int js=0;
for(RI k=1;k<=d;++k) js+=a[p[i]][k]*a[p[j]][k];
if(!(js%3))
{printf("%d %d\n",mii(p[i],p[j]),mxx(p[i],p[j]));return;}
}
}
puts("-1 -1");
}
int main()
{
srand(19260817);//选一个优秀的随机种子
n=read(),d=read(),K=read();
for(RI i=1;i<=n;++i)
for(RI j=1;j<=d;++j) a[i][j]=read()%K;
for(RI i=1;i<=n;++i) p[i]=i;
for(RI i=1;i<=n;++i) swap(p[i],p[rand()%n+1]);
if(K==2) work2();
else work3();
return 0;
}