省选模拟赛 厌世者打击(60分)
分析:码死我了这道题......
第一次用矩阵树定理+高斯消元做题. O(n^3)可以跑过前50%的点. 对于k = 1的点,直接输出1即可.关键是有一个取模操作......
一开始我用double存答案,因为最后要取绝对值,取模的话不好弄,干脆用double就能不取模了. 打完之后要存到long long中,发现会爆掉,于是用fmod函数取模,发现结果又不对.
正确的做法是利用行列式变换的性质. 行列式的两行交换会使答案取反,那么ans = -ans即可. 行列式的一行取反也会使得答案取反. 为了保证最后取模得到的结果是正确的. 需要保证高斯消元数组a中的每个元素都是 < mod并且 ≥ 0的. 当要消第i个元素时,将这一行全部取模. 如果其<0,则将这一行全部反转. 再来消. 每消一次就要换行一次.(有点类似于更相减损术中的大数-小数),答案也跟着变.这样利用long long就能存下答案了.
#include <cstdio> #include <cmath> #include <cstring> #include <iostream> #include <algorithm> using namespace std; typedef long long ll; const ll mod = 1e9+7; const double eps = 1e-9; double d[1010][1010],c[1010][1010]; ll n,k,ans,a[1010][1010]; void solve() { n--; ans = 1; for (int i = 1; i <= n; i++) { if (a[i][i] < 0) { for (int k = i; k <= n; k++) a[i][k] = -a[i][k]; ans = -ans; } for (int j = i + 1; j <= n; j++) { for (int k = i; k <= n; k++) a[i][k] %= mod,a[j][k] %= mod; while (a[j][i]) { if (a[j][i] < 0) { for (int k = i; k <= n; k++) a[j][k] = -a[j][k]; ans = -ans; } ll t = a[i][i] / a[j][i]; for (int k = i; k <= n; k++) a[i][k]=(a[i][k]-t*a[j][k]+mod)%mod; for (int k = i; k <= n; k++) swap(a[i][k],a[j][k]); ans = -ans; } } ans = ans * a[i][i] % mod; } ans = (ans % mod + mod) % mod; } int main() { scanf("%lld%lld",&n,&k); if (k == 1) printf("%d\n",1); else { for (int i = 2; i <= n; i++) { for (int j = 1; j <= k; j++) { int t = i - j; if (t <= 0) break; d[i][i]++; d[t][t]++; c[i][t]++; c[t][i]++; } } for (int i = 1; i <= n; i++) for (int j = 1; j <= n; j++) a[i][j] = d[i][j] - c[i][j]; solve(); printf("%lld\n",ans); } return 0; }