小T的数字(弱化版)

小T的数字(弱化版)

题目背景

小T很喜欢算法,尽管每天忙于实验室的各种项目,但它每天仍能抽出时间学习算法,真可谓是“劳逸结合”。

题目描述

但是最近,小T发现了一个很有意思的算法,它的题目是这样的:

在日常生活中, 8 8 8是自古以来备受喜爱的一个数字,但 4 4 4是自古以来不怎么喜欢的一个数字,因而,定义一个 A = ( a 1 , a 2 , a 3 , … … , a n ) A=(a_1,a_2,a_3,……,a_n) A=(a1,a2,a3,……,an)满足按照如下式子变换: a n = a n × 10 + 8 a_n=a_{n}\times 10+8 an=an×10+8

定义一个数列 B = ( b 1 , b 2 , b 3 , … … , b n ) B=(b_1,b_2,b_3,……,b_n) B=(b1,b2,b3,……,bn),满足 b i = a i × X b_i=a_i\times X bi=ai×X,定义 S = ∑ i = 1 n b i S=\sum_{i=1}^{n}b_i S=i=1nbi,要求求出 S S S的值。

但此时小T看到了这个公式,感到很不舒服,因为当 n n n很大的时候,加起来很可能有数字 4 4 4,因此小T想把所有的数字 4 4 4给去掉,因此它想知道去除掉(除法)数字 4 4 4 S S S为多少?

但好奇的小T远不及此,他还想知道给定数轴(这些数字可能很大且距离很远)默认已经赋了初值(初值是题目给定的),同时给定 L , R L,R L,R求出它的区间异或值,记为 P P P,求出此时的值。

小T数学不怎么好(其实只是在某些题目可能不怎么会做,但大体应该是会做的),身为他的朋友的小L和小D,要帮帮他。

输入格式

问题1:

第一行:为数组长度 N N N、问题类型(上述有两个问题) T T T X X X

第二行: A = ( a 1 , a 2 , a 3 , … … , a n ) A=(a_1,a_2,a_3,……,a_n) A=(a1,a2,a3,……,an)(也就是数组嘛)。

问题2:

第一行:输入查询数量 N N N、问题类型(上述有两个问题) T T T

2 2 2~ N + 2 N+2 N+2行:每行输入该一维坐标轴的 L L L位置的值为 R R R

接下来的 N + 3 N+3 N+3~ 2 N + 3 2N+3 2N+3行:每行输入 L , R L,R L,R数对,用来查询区间的异或和

输出格式

若为问题1,则输出 S S S的值。

若为问题2,每个询问输出 P P P的值。

样例 #1

样例输入 #1

6 1 2
779804187
373980902
982838151
6578324
638150707
936364479

样例输出 #1

18588583774

样例 #2

样例输入 #2

6 2
8 4
10 1
6 9
6 10
10 2
7 4
6 9
8 8
1 6
4 6
6 7
1 2

样例输出 #2

3
-4
3
3
7
0

提示说明

20 % 20\% 20%测试点满足: N < 10 , a i < 1 0 9 , b i < 1 0 9 N<10,a_i<10^9,b_i<10^9 N<10ai<109bi<109

40 % 40\% 40%测试点满足: N < 1 0 4 N<10^4 N<104 a a a的最大长度为 1000 1000 1000

40 % 40\% 40%测试点满足: N < 1 0 6 。 N<10^6。 N<106

L 、 R 、 a 、 b 均 < 1 0 9 L、R、a、b均<10^{9} LRab<109

测试数据均保证 > 0 >0 >0

坐标轴没有负的(让你们开心,要不然麻烦)。

温馨提示

  • 测试点的运行量有点大,尽量开一维数组
  • 由于问题2的处理数据较多,尽量使用scanf()与printf(),来节省输入输出时间!!
    在这里插入图片描述

注意:

  1. 区间异或值(也就是异或和)指的是: A l ⊕ A l + 1 ⊕ A l + 2 ⊕ … … A r A_l\oplus A_{l+1}\oplus A_{l+2}\oplus…… A_{r} AlAl+1Al+2……Ar,其中, l 、 r l、r lr为区间的左右端点。

  2. 对于问题2可能有一个坐标点对应多个值,因此我们应该先把它们异或完再放到该点上。也就是说当出现一个坐标点对应多个值的时候,应该先把它们都异或起来即可。

针对第2点举个例子:

6这个点上有10,9两个数,那么我们先得把10^9得到3放到6这个点上去。


耗时 5 h 5h 5h出完本道题。

为什么是 1 0 9 10^9 109呢,因为本人测试, 1 0 18 10^{18} 1018这种考察方式太费内存了,因此干脆不搞了。

嘻嘻,小T原形tyb。

希望你们都能做的起!!!

本人感觉相比上一次出的题,本次题的界面更加美观并且数据更加+随机。

测试用例文件

代码内容

// #include <iostream>
// #include <vector>//容器,存数组的数,表数组的长度
#include <bits/stdc++.h>

using namespace std;
typedef long long ll;
typedef pair<ll,ll> PII;

const ll N=3e6+10;
ll a[N],s[N];

vector<ll> alls;//存入下标容器
vector<PII> add,query;//add增加容器,存入对应下标和增加的值的大小
//query存入需要计算下标区间和的容器


//A/B,商是C,余数为r
vector<ll> div(vector<ll> &C)
{
    vector<ll> S;
    ll r=0;
    for (ll i=C.size()-1;i>=0;i--)
    {
        r=r*10+C[i];
        S.push_back(r/4);
        r%=4;
    }
    
    reverse(S.begin(),S.end());//逆排序
    //去除前导0
    while(S.size()>1&&S.back()==0) S.pop_back();
    return S;
}

vector<ll> mul(vector<ll> &A,ll x)
{
    vector<ll> B;

    ll t=0;
    for(ll i=0;i<A.size()||t;i++)
    {
        if(i<A.size()) t+=A[i]*x;
        B.push_back(t%10);
        t/=10;
    }

    return B;
}

vector<ll> adds(vector<ll> &A,vector<ll> &B)
{
    vector<ll> C;
    ll t=0;//进位
    
    for(ll i=0;i<A.size()||i<B.size();i++)
    {
        if(i<A.size()) t+=A[i];
        if(i<B.size()) t+=B[i];
        C.push_back(t%10);
        t/=10;
    }

    if(t==1) C.push_back(t);
    return C;
}

void solve1(ll n,ll x)
{
    vector<ll> C;
    C.push_back(0);

    while(n--)
    {
        string a;
        vector<ll> A,B;
        cin>>a;//a={1,2,3,4,5};
        A.push_back(8);//a=a*10+8;
        
        //数倒序依次存入,容器数组B下标以0依次增加
        for(ll i=a.size()-1;i>=0;i--)
        A.push_back(a[i]-'0');//A={8,5,4,3,2,1};
        B=mul(A,x);
        C=adds(B,C);
    }

    auto S=div(C);//auto 表示编译器自动识别c的变量的数据类型
    for(ll i=S.size()-1;i>=0;i--)
        cout<<S[i];

    return;
}

ll find(ll x)
{
    ll l=0,r=alls.size()-1;
    while(l<r)//查找大于等于x的最小的值的下标
    {
        ll mid=l+r>>1;
        if(alls[mid]>=x) r=mid;
        else l=mid+1;
    }
    return r+1;//因为使用前缀和,其下标要+1可以不考虑边界问题
}

void solve2(ll n)
{
    ll l,r;
    for(ll i=0;i<n;i++)
    {
        //cin>>l>>r;
        //cin输入数据较慢
        //由于输入数据较多,故用scanf()输入,节省输入时间!!
        scanf("%lld%lld",&l,&r);
        add.push_back({l,r});//存入下标即对应的数值c

        alls.push_back(l);//存入数组下标x=add.first
    }

    for(ll i=0;i<n;i++)
    {
        //cin>>l>>r;
        //cin输入数据较慢
        //由于输入数据较多,故用scanf()输入,节省输入时间!!
        scanf("%lld%lld",&l,&r);
        query.push_back({l,r});//存入要求的区间

        alls.push_back(l);//存入区间左右下标
        alls.push_back(r);
    }

    // 区间去重
    sort(alls.begin(),alls.end());
    alls.erase(unique(alls.begin(),alls.end()),alls.end());

    // 处理插入
    for(auto item:add)
    {
        ll x=find(item.first);//将add容器的add.secend值存入数组a[]当中
        //在去重之后的下标集合alls内寻找对应的下标所对应的值与添加数值的异或值
        a[x]^=item.second;
    }

    // 预处理前缀和
    for(ll i=1;i<=alls.size();i++)
        s[i]=s[i-1]^a[i];//前缀和的异或值

    // 处理询问
    for(auto item:query)
    {
        ll l=find(item.first),r=find(item.second);//在下标容器中查找对应的左右两端[l~r]下标,然后通过下标得到前缀和相减再得到区间a[l~r]的和
        //cout<<s[r]-s[l-1]<<endl;
        //cout输出数据较慢
        //由于输出数据较多,故用printf()输出,节省输出时间!!
        printf("%lld\n",s[r]-s[l-1]);
    }

    return;
}

int main()
{
    ll n,t,x;
    cin>>n>>t;

    if(t==1)
    {
        cin>>x;
        solve1(n,x);
    }
    else if(t==2)
        solve2(n);

    return 0;
}

  • 63
    点赞
  • 45
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Pretty Boy Fox

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值