链接
https://uva.onlinejudge.org/index.php?option=com_onlinejudge&Itemid=8&page=show_problem&problem=4078
题解
先列出一个线性方程组(代数系统为模代数系统)
然后开始消元
注意
n
n
n不一定是素数,因此可能没有逆元
那我消元的时候就不能做除法,只能做乘法
但是做乘法会产生增根,比如以下例子:
x
+
y
=
1
(
m
o
d
  
2
)
x+y=1(\mod 2)
x+y=1(mod2)
如果我给方程所有系数扩大
2
2
2,得到
2
x
+
2
y
=
2
(
m
o
d
  
2
)
2x+2y=2(\mod2)
2x+2y=2(mod2)
显然第二个方程相当于
0
x
+
0
y
=
0
(
m
o
d
  
2
)
0x+0y=0(\mod 2)
0x+0y=0(mod2)
解集扩大了
我扩大的倍数越多,带来的增根就可能越多,增根的问题我可以最后用一步回代来解决
主要是我如何让增根尽量少?
其实就是尽量让扩大的倍数小一点
用lcm肯定是一个很直接的想法,但是实际证明这样会让方程的增根变得很多很多,可能导致超时
问了万能的UOJ群之后得到一个辗转相除的做法qwq
真是神了
详见代码吧,就是用类似于辗转相除的方法把其中一个消为
0
0
0
辗转相除其实和更相减损是同一个东西
直觉上这样比用lcm要更好一些
解的枚举就是爆搜,一个很不错的方法就是倒着搜
消元之后不是得到一个右上三角吗?
我从最后一个元素开始枚举,这样每次消掉一整列,直觉上矛盾会较早出现,也就能更早的剪枝
虽然最后不需要验证增根就能过这道题,但是理论上讲,最好是要回代一下
代码
#include <bits/stdc++.h>
#define maxn 21
#define eps 1e-8
#define linf (1ll<<60)
#define cl(x) memset(x,0,sizeof(x))
using namespace std;
typedef long long ll;
ll n, m, a, b, ans;
ll read(ll x=0)
{
ll c, f=1;
for(c=getchar();!isdigit(c);c=getchar())if(c=='-')f=-f;
for(;isdigit(c);c=getchar())x=x*10+c-48;
return f*x;
}
ll val[maxn], N, K;
struct matrix
{
ll n, m;
ll a[maxn][maxn], res[maxn];
matrix(ll n, ll m):n(n),m(m){cl(a);cl(res);}
ll* operator[](const ll& index){return a[index];}
void gauss()
{
ll i, j, k, r;
for(i=1;i<=n;i++)
{
r=i;
for(j=r+1;j<=n;j++)if(a[j][i])r=j;
if(a[r][i]==0)continue;
for(j=1;j<=n+1;j++)swap(a[i][j],a[r][j]);
for(j=i+1;j<=n;j++)
{
while(a[j][i])
{
ll rate(a[i][i]/a[j][i]), t;
for(k=1;k<=n+1;k++)
{
t=a[j][k];
a[j][k]=( (a[i][k] - rate*a[j][k] ) %N + N ) % N;
a[i][k]=t;
}
}
}
}
}
void show()
{
for(ll i=1;i<=n;i++)for(ll j=1;j<=n+1;j++)printf("%2lld",a[i][j]), putchar(j==n+1?10:32);
}
};
void dfs(ll now, matrix &M, matrix &O, ll s)
{
ll i, j;
if(s>ans)return;
for(i=1;i<=M.n;i++)
{
if(M.res[i]==0)
{
ll s=0;
for(j=now+1;j<=M.n;j++)s+=M[i][j]*val[j];
if(s%N != M[i][M.n+1]%N)return;
}
}
if(now == 0)
{
for(i=1;i<=O.n;i++)
{
ll s=0;
for(j=1;j<=O.n;j++)
{
s+=O[i][j]*val[j];
}
if(s%N != O[i][O.n+1]%N)return;
}
ans=min(ans,s);
return;
}
for(i=1;i<=M.n;i++)if(M[i][now])M.res[i]--;
for(val[now]=0;val[now]<N;val[now]++)
{
dfs(now-1,M,O,s+val[now]);
}
for(i=1;i<=M.n;i++)if(M[i][now])M.res[i]++;
}
int main()
{
ll i, j, k, a, b, p, x;
while(K=read(), N=read())
{
matrix T(K,K+1);
for(i=1;i<=K;i++)T[i][K+1]=N-read()+1;
for(i=1;i<=K;i++)
{
p=read();
for(j=1;j<=p;j++)
{
a=read(), b=read();
T[a][i]=b;
}
}
matrix Orign(T);
T.gauss();
for(i=1;i<=K;i++)
{
for(j=1;j<=K;j++)if(T[i][j])T.res[i]++;
}
ans=linf;
dfs(K,T,Orign,0);
if(ans==linf)printf("No solution\n");
else printf("%lld\n",ans);
}
return 0;
}