题解 亿点整理
神仙题!
听说大力卡时网络流匹配能过
考虑就是要找一个排列 \(p\) 使 \(\sum f_{i, p_i}\equiv 0 \pmod k\)
考虑将矩阵中的每个元素写成集合幂级数 \(x^{f_{i, j}}\),当 \(f_{i, j}=0\) 时这个元素就直接赋为 0
然后将乘法定义为 \(\bmod k\) 意义下的循环卷积
那么求出行列式,则有解的条件是 \([x^0]\det\neq0\)……吗?
显然不是,因为行列式前面 \((-1)^{\pi(p)}\) 的系数可能相消成 0
那么考虑给多项式每项系数再乘个随机权值,这样正确率就比较高了
直接多项式操作的复杂度是 \(O(n^4\log n)\) 的,过不去
考虑怎么在更优秀的复杂度内求出 \(\det\)
发现这是个 \(k-1\) 次多项式,那么求出 \(k\) 个点值带进去 lagrange 插值即可
然而还有更简单的做法
考虑利用单位根反演的一些性质
发现我们要选 \(k\) 个点值带进去,要求的是 \([x^0]\det\) 是否为 0
利用 \([n|1]=\frac{1}{n}\sum\limits_{i=0}^{n-1}\omega_n^{i}\)
考虑将单位根的 \([0, k-1]\) 次方当做 \(x\) 带进去
令多项式系数为 \(b_0\sim b_{k-1}\),点值为 \(a_0\sim a_{k-1}\)
有
\[a_i=\sum x_i^j b_j
\]
那么
\[\begin{aligned}\sum a_i&=\sum b_i\sum x_j^i \\ &=b_0\times k+\sum\limits_{i\geqslant 1}b_i\sum x_j^i \end{aligned}
\]
发现 \(\sum x_j^i=\sum\limits_{j=0}^{k-1}\omega_n^{ij}=[k|i]=0\)
所以只要将点值简单加和判断是否为 0 就可以了
p.s. 感谢可爱雨兔兔和 zxy 的模数表!
复杂度 \(O(n^4)\)
点击查看代码
#include <bits/stdc++.h>
using namespace std;
#define INF 0x3f3f3f3f
#define N 110
#define ll long long
//#define int long long
char buf[1<<21], *p1=buf, *p2=buf;
#define getchar() (p1==p2&&(p2=(p1=buf)+fread(buf, 1, 1<<21, stdin)), p1==p2?EOF:*p1++)
inline int read() {
int ans=0, f=1; char c=getchar();
while (!isdigit(c)) {if (c=='-') f=-f; c=getchar();}
while (isdigit(c)) {ans=(ans<<3)+(ans<<1)+(c^48); c=getchar();}
return ans*f;
}
int n, k;
int a[N][N];
namespace force{
bool f[1<<20][200];
void solve() {
// cout<<double(sizeof(f))/1000/1000<<endl;
int lim=1<<n;
f[0][0]=1;
for (int s=0,cnt; s<lim; ++s) {
cnt=__builtin_popcount(s);
for (int i=0; i<k; ++i) if (f[s][i])
for (int j=0; j<n; ++j) if (!(s&(1<<j)) && a[cnt][j]!=-1)
f[s|(1<<j)][(i+a[cnt][j])%k]|=f[s][i];
}
puts(f[lim-1][0]?"Yes":"No");
}
}
namespace task{
ll rnd[N][N], rt, mod, w;
const int M[] = { 0, 0, 1073741827, 1073741827, 1073741833, 1073741831, 1073741827, 1073741831, 1073741833, 1073741833, 1073741831, 1073741857, 1073741833, 1073741839, 1073741831, 1073741971, 1073741857, 1073742169, 1073741833, 1073741833, 1073742361, 1073741971, 1073741857, 1073741891, 1073741833, 1073742851, 1073741839, 1073741833, 1073742209, 1073742053, 1073741971, 1073742289, 1073741857, 1073741857, 1073742169, 1073741831, 1073741833, 1073742073, 1073741833, 1073741839, 1073742361, 1073742113, 1073741971, 1073741993, 1073741857, 1073742391, 1073741891, 1073742073, 1073741857, 1073742391, 1073742851, 1073742169, 1073741969, 1073742053, 1073741833, 1073742671, 1073742209, 1073741833, 1073742053, 1073741827, 1073742361, 1073747621, 1073742289, 1073742391, 1073741953, 1073741891, 1073741857, 1073742403, 1073742169, 1073742259, 1073741831, 1073745781, 1073741833, 1073743861, 1073742073, 1073743051, 1073741833, 1073742209, 1073741839, 1073742721, 1073742721, 1073741833, 1073742113, 1073745769, 1073742517, 1073743291, 1073741993, 1073742169, 1073741857, 1073743883, 1073742391, 1073742671, 1073742673, 1073742289, 1073742073, 1073744911, 1073741857, 1073742277, 1073742391, 1073742517, 1073743501 };
const int G[] = { 0, 0, 2, 2, 5, 13, 2, 13, 5, 5, 13, 5, 5, 3, 13, 2, 5, 7, 5, 5, 7, 2, 5, 6, 5, 2, 3, 5, 3, 2, 2, 37, 5, 5, 7, 13, 5, 5, 5, 3, 7, 5, 2, 3, 5, 6, 6, 5, 5, 6, 2, 7, 3, 2, 5, 7, 3, 5, 2, 2, 7, 2, 37, 6, 10, 6, 5, 2, 7, 2, 13, 2, 5, 6, 5, 2, 5, 3, 3, 11, 11, 5, 5, 7, 6, 2, 3, 7, 5, 2, 6, 7, 5, 37, 5, 6, 5, 5, 6, 6, 2 };
inline ll qpow(ll a, ll b) {ll ans=1; for (; b; a=a*a%mod,b>>=1) if (b&1) ans=ans*a%mod; return ans;}
struct matrix{
int n, m;
ll a[N][N];
matrix() {n=m=0; memset(a, 0, sizeof(a));}
matrix(int x, int y) {n=x; m=y; memset(a, 0, sizeof(a));}
void resize(int x, int y) {n=x; m=y; memset(a, 0, sizeof(a));}
inline ll* operator [] (int t) {return a[t];}
inline void put() {for (int i=1; i<=n; ++i) {for (int j=1; j<=n; ++j) cout<<setw(10)<<a[i][j]<<' '; cout<<endl;}cout<<endl;}
ll qdet() {
ll ans=1;
for (int i=1; i<=n; ++i) {
for (int j=i+1; j<=n; ++j) {
while (a[j][i]) {
ll t=a[i][i]/a[j][i];
for (int k=i; k<=m; ++k) a[i][k]=(a[i][k]-a[j][k]*t)%mod;
swap(a[i], a[j]); ans*=-1;
}
}
}
for (int i=1; i<=n; ++i) ans=ans*a[i][i]%mod;
return ans;
}
}mat;
void solve() {
random_device seed;
mt19937 rand(seed());
rt=G[k]; mod=M[k]; w=qpow(rt, (mod-1)/k);
ll x=w, sum=0;
mat.resize(n, n);
for (int i=1; i<=n; ++i) for (int j=1; j<=n; ++j) rnd[i][j]=rand()%mod;
for (int i=1; i<=k; ++i,x=x*w%mod) {
// cout<<"x: "<<x<<endl;
for (int j=1; j<=n; ++j)
for (int k=1; k<=n; ++k)
mat[j][k]=~a[j][k]?qpow(x, a[j][k])*rnd[j][k]%mod:0;
// mat.put();
sum=(sum+mat.qdet())%mod;
}
puts(sum?"Yes":"No");
}
}
signed main()
{
freopen("sort.in", "r", stdin);
freopen("sort.out", "w", stdout);
n=read(); k=read();
for (int i=1; i<=n; ++i) for (int j=1; j<=n; ++j) a[i][j]=read();
// force::solve();
task::solve();
return 0;
}