[BZOJ 2854] civilization 高斯消元

题意

  给定 $n$, $A_{n\times n}, B = \left\{ b_i \right\}$ .

  解方程 $Ax = B^T$ .

  $n \le 200, A_{ij} \in [- {10} ^ 9, {10} ^ 9]$ .

  保证 $x_i \in [- {10} ^ {18}, {10} ^ {18}]$ .

分析

  大素数取模下高斯消元, 通过 CRT 进行合并.

  http://blog.csdn.net/owen_hzt/article/details/41493637

实现

  首先, 我们测试一下 long long 大致可以存到多少.

  long long 的最大值大约是 $9 \times {10} ^ {18}$ .

  我们要选取两个合适的大素数取模, 然后进行 CRT .

  保证这两个素数大于 ${10} ^ 9$ , 相乘大于 ${10} ^ {18}$ , 且不会超过 long long 的最大值.

  而且, 这两个素数要保证系数矩阵满秩.

  系数矩阵是否满秩, 必须要通过高斯消元来发现, 所以我们先准备多几个素数, 取能成功的前两个.

  我们可以写一个判断是否是素数的程序, 进行检验.

#include <cstdio>
#include <cstring>
#include <cstdlib>
#include <cctype>

inline bool Prime(int x) {
    if (x <= 1) return false;
    for (int i = 2; i * i <= x; i++)
        if (x % i == 0) return false;
    return true;
}

int main(void) {
    for (int x; ~scanf("%d", &x); )
        puts(Prime(x) ? "YES" : "NO");
    return 0;
}

  我们找到了这样几个数: 1000000007, 1000000009, 1000000021, 1000000087.

  最后合并答案的时候注意要写快速乘法.

  因为对于 mod 一个 long long 大小的数的操作, 直接乘会爆 long long !

  最终代码:

#include <cstdio>
#include <cstring>
#include <cstdlib>
#include <cctype>
#include <algorithm>
using namespace std;

#define F(i, a, b) for (register int i = (a); i <= (b); i++)

#define LL long long

const int N = 205;
const LL P[4] = {(int)1e9+7, (int)1e9+9, (int)1e9+21, (int)1e9+87};

int n; LL A[N][N], B[N][4];
LL X[N][4]; int z1, z2;

inline LL rd(void) {
    LL f = 1; char c = getchar(); for (; !isdigit(c); c = getchar()) if (c == ‘-‘) f = -1;
    LL x = 0; for (; isdigit(c); c = getchar()) x = x*10+c-‘0‘; return x*f;
}

LL Inv(LL x, LL M) { LL res = 1; for (LL y = M-2; y > 0; y >>= 1, x = x * x % M) if (y & 1) res = res * x % M; return res; }
bool Gauss(int K) {
    LL M = P[K];
    static LL a[N][N];
    F(i, 1, n) {
        F(j, 1, n) a[i][j] = A[i][j];
        a[i][n+1] = B[i][K];
    }

    F(i, 1, n) {
        if (!a[i][i]) {
            bool G = false;
            for (int j = i+1; j <= n && !G; j++) if (a[j][i] > 0) {
                G = true;
                F(k, 1, n+1) swap(a[i][k], a[j][k]);
            }
            if (!G) return false;
        }
        LL R = Inv(a[i][i], M);
        F(j, 1, n) if (i != j && a[j][i] > 0) {
            LL tmp = R * a[j][i] % M;
            F(k, 1, n+1)
                a[j][k] = ((a[j][k] - a[i][k] * tmp) % M + M) % M;
        }
    }

    F(i, 1, n) {
        LL R = Inv(a[i][i], M);
        X[i][K] = a[i][n+1] * R % M;
    }
    return true;
}

inline LL Mul(LL x, LL y, LL P) {
    LL res = 0;
    for (; y > 0; y >>= 1, x = (x + x) % P)
        if (y & 1) res = (res + x) % P;
    return res;
}
inline void Print(LL X1, LL P1, LL X2, LL P2) {
    printf("%lld\n", (Mul(X1 * P2, Inv(P2 % P1, P1), P1 * P2) + Mul(X2 * P1, Inv(P1 % P2, P2), P1 * P2)) % (P1 * P2));
}

int main(void) {
    #ifndef ONLINE_JUDGE
        freopen("bzoj2854.in", "r", stdin);
        freopen("bzoj2854.out", "w", stdout);
    #endif

    n = rd();
    F(i, 1, n) {
        F(j, 1, n) A[i][j] = rd();
        static char s[50]; scanf("%s", s+1);
        for (int j = 0, L = strlen(s+1); j < 4; j++)
            F(k, 1, L) B[i][j] = (B[i][j] * 10 + s[k] - ‘0‘) % P[j];
    }

    z1 = z2 = -1;
    for (int k = 0; k < 4; k++) {
        if (!Gauss(k)) continue;
        if (z1 == -1) z1 = k;
        else if (z2 == -1) z2 = k;
    }

    F(i, 1, n)
        Print(X[i][z1], P[z1], X[i][z2], P[z2]);

    return 0;
}

  

时间: 2024-10-11 20:59:34

[BZOJ 2854] civilization 高斯消元的相关文章

BZOJ 2419 电阻 高斯消元

题目大意:给定n个点,一些点之间有电阻相连,求1~n的等效电阻 首先我们设电流为1A 终点电势为零 点i的电势为Ui 由于电流是流 显然对于每个点(点1和点n除外) 有总流入等于总流出 即 Σ(Ui-Uj)/Rij=0 (i!=1,i!=n) Σ(U1-Uj)/R1j=1 Σ(Un-Uj)/Rnj=-1 Un=0 联立方程组高斯消元即可 最后输出点1的电势就是答案 注意自环要无视 重边要用倒数和缩成一条 我用了电导所以直接加就行 科普:电导 电阻的倒数 G=1/R=I/U 在两个点之间并联多个电

BZOJ 3143 HNOI2013 游走 高斯消元 期望

这道题是我第一次使用高斯消元解决期望类的问题,首发A了,感觉爽爽的.... 中文题目,就不翻大意了,直接给原题: 一个无向连通图,顶点从1编号到N,边从1编号到M. 小Z在该图上进行随机游走,初始时小Z在1号顶点,每一步小Z以相等的概率随机选 择当前顶点的某条边,沿着这条边走到下一个顶点,获得等于这条边的编号的分数.当小Z 到达N号顶点时游走结束,总分为所有获得的分数之和. 现在,请你对这M条边进行编号,使得小Z获得的总分的期望值最小. 输出最小的总分期望值. Solution: 这题贪心很明显

[BZOJ 3143][Hnoi2013]游走(高斯消元+期望)

Description 一个无向连通图,顶点从1编号到N,边从1编号到M. 小Z在该图上进行随机游走,初始时小Z在1号顶点,每一步小Z以相等的概率随机选 择当前顶点的某条边,沿着这条边走到下一个顶点,获得等于这条边的编号的分数.当小Z 到达N号顶点时游走结束,总分为所有获得的分数之和. 现在,请你对这M条边进行编号,使得小Z获得的总分的期望值最小. Solution 对于点u(u≠1):到达u的概率 f[u]=∑f[v]/d[v] (Edges(u,v)) 而f[1]=∑f[v]/d[v]+1

【BZOJ 4171】 4171: Rhl的游戏 (高斯消元)

4171: Rhl的游戏 Time Limit: 20 Sec  Memory Limit: 128 MBSubmit: 74  Solved: 33[Submit][Status][Discuss] Description RHL最近迷上一个小游戏:Flip it.游戏的规则很简单,在一个N*M的格子上,有一些格子是黑色,有一些是白色 .每选择一个格子按一次,格子以及周围边相邻的格子都会翻转颜色(边相邻指至少与该格子有一条公共边的格子 ),黑变白,白变黑.RHL希望把所有格子都变成白色的.不幸

BZOJ 3105: [cqoi2013]新Nim游戏 [高斯消元XOR 线性基]

以后我也要用传送门! 题意:一些数,选择一个权值最大的异或和不为0的集合 终于有点明白线性基是什么了...等会再整理 求一个权值最大的线性无关子集 线性无关子集满足拟阵的性质,贪心选择权值最大的,用高斯消元判断是否和已选择的线性相关 每一位记录pivot[i]为i用到的行 枚举要加入的数字的每一个二进制为1的位,如果有pivot[i]那么就异或一下(消元),否则pivot[i]=这个数并退出 如果最后异或成0了就说明线性相关... #include <iostream> #include &l

【BZOJ 1013】【JSOI2008】球形空间产生器sphere 高斯消元基础题

最基础的高斯消元了,然而我把j打成i连WA连跪,考场上再犯这种错误就真的得滚粗了. #include<cmath> #include<cstdio> #include<cstring> #include<algorithm> #define for1(i,a,n) for(int i=(a);i<=(n);++i) #define for2(i,a,n) for(int i=(a);i<(n);++i) #define for3(i,a,n) f

【概率DP/高斯消元】BZOJ 2337:[HNOI2011]XOR和路径

2337: [HNOI2011]XOR和路径 Time Limit: 10 Sec  Memory Limit: 128 MBSubmit: 682  Solved: 384[Submit][Status][Discuss] Description 几乎是一路看题解过来了.. 拖了一个星期的题目- - 已然不会概率DP(说得好像什么时候会过一样),高斯消元(打一次copy一遍). 发现异或题目的新解决方法:按位处理.. 发现DP新方法:高斯消元. f[k][i]代表第k位权值起点为i到终点时答案

BZOJ 1013 [JSOI2008]球形空间产生器sphere 【高斯消元】

Description 有一个球形空间产生器能够在n维空间中产生一个坚硬的球体.现在,你被困在了这个n维球体中,你只知道球面上n+1个点的坐标,你需要以最快的速度确定这个n维球体的球心坐标,以便于摧毁这个球形空间产生器. HINT 1<=n<=10 提示:给出两个定义:1. 球心:到球面上任意一点距离都相等的点.2. 距离:设两个n为空间上的点A, B的坐标为(a1, a2, …, an), (b1, b2, …, bn),则AB的距离定义为:dist = sqrt( (a1-b1)^2 +

BZOJ 1013 JSOI2008 球形空间产生器sphere 高斯消元

题目大意:给定n维空间下的n+1个点,求这n个点所在的球面的球心 曾经尝试了很久的模拟退火0.0 至今仍未AC 0.0 今天挖粪涂墙怒学了高斯消元-- 我们设球心为X(x1,x2,...,xn) 假设有两点A(a1,a2,...,an)和B(b1,b2,...,bn) 那么我们可以得到两个方程 (x1-a1)^2+(x2-a2)^2+...+(xn-an)^2=r^2 (x1-b1)^2+(x2-b2)^2+...+(xn-bn)^2=r^2 这些方程都是二次的,无法套用高斯消元 但是我们可以做