高精度,前缀和与差分

高精度:

概念:处理内置类型(int、long long)存不下的数

用将大数字当字符,.数组是反着放的,即高位数放数组后面(方便进位和去前导0)

①高精度加法:

模版:

string a, b;    //将数组当字符存
vector<int>j, k;
vector<int>add(vector<int>& j, vector<int>& k)
{
    vector<int>tmp; int t = 0;     //tmp为临时数组
    for (int i = 0; i < j.size() || i < k.size(); i++)     //最长那一个数组结束了才结束循环
    {
        if (i < j.size())t += j[i];
        if (i < k.size())t += k[i];     //判断本数组结束没有
        tmp.push_back(t % 10);
        t /= 10;//对下一位的余数
    }
    if (t)tmp.push_back(t);     //这里t只有0,1两种情况
    return tmp;
}
int main()
{
    cin >> a >> b;
    for (int i = a.size() - 1; i >= 0; i--)j.push_back(a[i] - '0');
    for (int i = b.size() - 1; i >= 0; i--)k.push_back(b[i] - '0');
        auto ans = add(j, k);
    for (int i = ans.size() - 1; i >= 0; i--)
        cout << ans[i];
    return 0;
}

②高精度减法
string a, b;
vector<int>j, k;
bool cmp(vector<int>j, vector<int>k)     //判断数j是否比数k大
{
    if (j.size() != k.size())return j.size() > k.size();     //长度不相等,长的大
    for (int i = j.size() - 1; i >= 0; i--)
        if (j[i] != k[i])return j[i] > k[i];     //长度相等,从大位比到小位
    return true;     //完全相等,也当j大
}
vector<int> sub(vector<int>j, vector<int>k)
{
    vector<int> tmp; int t = 0;     //t有进位和临时数两作用
    for (int i = 0; i < j.size(); i++)
    {
        t = j[i] - t;     //t原先为进位,操作后变临时数
        if (i < k.size())t -= k[i];     //临时数减减数
        tmp.push_back((t + 10) % 10);     //不管是否借位都+10,因为%10后一样
        if (t < 0)t = 1;
        else t = 0;     //两行判断是否借了数
    }
    while (tmp.size() > 1 && tmp.back() == 0)tmp.pop_back();     //去前导0
    return tmp;
}
int main()
{
    cin >> a >> b;
    for (int i = a.size() - 1; i >= 0; i--)j.push_back(a[i] - '0');
    for (int i = b.size() - 1; i >= 0; i--)k.push_back(b[i] - '0');
    if (cmp(j, k))     //因为函数前参一定要大于后参,所以要判断
    {
        auto ans = sub(j, k);
        for (int i = ans.size() - 1; i >= 0; i--)
            cout << ans[i];
    }
    else
    {
        auto ans = sub(k, j);
        cout << '-';     //a-b=-(b-a)转换
        for (int i = ans.size() - 1; i >= 0; i--)
            cout << ans[i];
    }
    return 0;
}

注意点:

1.如注释

2.若两个数中有负数,用string存好-号后当正数进行操作,最后转换结果就行

③高精度乘低精度

string a;
vector<int> j;
int k;
vector<int> mul(vector<int> j, int k)
{
    vector<int> tmp; int t = 0;
    for (int i = 0; i < j.size() || t; i++)     //t不为0时不能停
    {
        if (i < j.size())t += j[i] * k;     //判断是否执行完高精数
        tmp.push_back(t % 10);
        t /= 10;     //模拟进位
    }
    return tmp;
}
int main()
{
    cin >> a >> k;
    for (int i = a.size() - 1; i >= 0; i--) j.push_back(a[i] - '0');
    auto ans = mul(j, k);
    while (ans.size() > 1 && ans.back() == 0) ans.pop_back();     //去前导0(这里好像不用)
    for (int i = ans.size() - 1; i >= 0; i--) cout << ans[i];

}

④高精度除低精度

string a;
vector<int> j; int k; int r = 0;     //r是结果余数
vector<int> div(vector<int> j, int k, int& r)
{
    vector<int> tmp; int t = 0;     //t是上一位余下来的数
    for (int i = j.size() - 1; i >= 0; i--)
    {
        t = t * 10 + j[i];     //因为来自上一位,所以*10
        tmp.push_back(t / k);
        t %= k;
    }
    reverse(tmp.begin(), tmp.end());     //结果高位在前,所以将其反过来,方便去前导0
    r = t;
    return tmp;
}
int main()
{
    cin >> a >> k;
    for (int i = a.size() - 1; i >= 0; i--) j.push_back(a[i] - '0');
    auto ans = div(j, k, r);
    while (ans.size() > 1 && ans.back() == 0) ans.pop_back();     //去前导0
    for (int i = ans.size() - 1; i >= 0; i--) cout << ans[i];
    cout << endl << r;

}

前缀和:

原理:数组中存放另一数组相同位置前面所有数的和

作用:快速求出数组一段区间的和

const int s = 1e6 + 10;
int j[s], k[s];
int n, q;
int main()
{
    ios::sync_with_stdio(false);
    cin >> n >> q;
    for (int i = 1; i <= n; i++)     //从1开始,统一求区间和的形式
    {
        cin >> j[i];
        k[i] = k[i - 1] + j[i];
    }
    for (int i = 0; i < q; i++)
    {
        int x, y, ans;
        cin >> x >> y;
        ans = k[y] - k[x - 1];     //从1开始就不用特判0了
        cout << ans << endl;
    }
}

升级为二维

前缀和数组k中每个位置上的数都是数组j对应位置左上端点围成的所有数的和

const int s = 1e3+10;
int j[s][s], k[s][s];
int n, m, q;
int main()
{
    ios::sync_with_stdio(false);
    cin >> n >> m >> q;
    for (int i = 1; i <= n; i++)
        for (int z = 1; z <= m; z++)     //两层循环创建前缀和数组
        {
            cin >> j[i][z];
            k[i][z] = k[i - 1][z] + k[i][z - 1] - k[i - 1][z - 1] + j[i][z];     //减k[i - 1][z - 1]是因为其被加了两次
        }
    for (int i = 0; i < q; i++)
    {
        int x1, x2, y1, y2, ans = 0;
        cin >> x1 >> y1 >> x2 >> y2;     //求一个面的数之和要定两个点
        ans = k[x2][y2] - k[x2][y1 - 1] - k[x1 - 1][y2] + k[x1 - 1][y1 - 1];     //k[x1 - 1][y1 - 1]被减了两次
        cout << ans << endl;
    }

}

注意点:

二维前缀和和一维前缀和都从1开始建立,留空方便统一格式

差分

前缀和的逆运算,原数组从头加到制定位置建立前缀和数组,差分从头加到指定位置生成原数组

我们不关心差分怎么从原数组生成(其实是An-An-1),关心的是改变差分数组再用其生成原数组从而达到改变原数组的目的

用处:可以快速修改原数组的一段区间

一维模版:

const int s = 1e6+10;
int j[s], k[s];
int n, q;
void insert(int l, int r, int v)     //表明怎么改变差分数组从而使原数组的一段区间都改变
{
    k[l] += v;
    k[r + 1] -= v;     //在差分区间头尾改一次就行
}
int main()
{
    ios::sync_with_stdio(false);
    cin >> n >> q;
    for (int i = 1; i <= n; i++)
    {
        cin >> j[i];
        insert(i, i, j[i]);     //将原数组看成全0,生成原数组相当于原地插入数字(只改变一个数字)
    }
    for (int i = 1; i <= q; i++)
    {
        int l, r, v;
        cin >> l >> r >> v;
        insert(l, r, v);     //在原数组始末为l、r的区间都加上v
    }
    for (int i = 1; i <= n; i++) k[i] += k[i - 1];     //生成原数组
    for (int i = 1; i <= n; i++) cout << k[i] << ' ';
}

注意点:

改变一维差分数组的一个数,相当于对原数组这个位置以后的数进行相同操作

二维模版:

const int s = 1e3 + 10;
int j[s][s], k[s][s];
int r, c, q;
void insert(int x1, int y1, int x2, int y2, int v)     //改变原数组一片区域的数
{
    k[x1][y1] += v;
    k[x1][y2 + 1] -= v;
    k[x2 + 1][y1] -= v;
    k[x2 + 1][y2 + 1] += v;
}
int main()
{
    cin >> r >> c >> q;
    for (int i = 1; i <= r; i++)
        for (int z = 1; z <= c; z++)
        {
            cin >> j[i][z];
            insert(i, z, i, z, j[i][z]);     //和一维一样,原地插入数字
        }
    for (int i = 1; i <= q; i++)
    {
        int x1, y1, x2, y2, v;
        cin >> x1 >> y1 >> x2 >> y2 >> v;
        insert(x1, y1, x2, y2, v);
    }
    for (int i = 1; i <= r; i++)
        for (int z = 1; z <= c; z++)
        {
            k[i][z] += k[i - 1][z] + k[i][z - 1] - k[i - 1][z - 1];     //生成原数组
        }
    for (int i = 1; i <= r; i++)
    {
        for (int z = 1; z <= c; z++)
            cout << k[i][z] << ' ';
        cout << endl;
    }
    return 0;
}
注意点:

二维差分数组修改某一位置的数相当于对原数组该位置和右下角围成的矩形内的所有数进行相同操作

小白拙见,欢迎提出修改意见~~~


 

  • 20
    点赞
  • 23
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值