2018 上海大都会赛 H、A Simple Problem with Integers(带暴力成分的线段树)

题目链接

题意:对数组进行两个操作:
1、C L R 修改区间[L,R]上每个值为本身的平方,取mod 2018
2、Q L R 查询区间[L,R]的数值和


说说感受吧! 这道题,我暴力打表,在2018内的数分为两种:一种自幂的所有数中初始段不会出现第两次,后面会形成环;另外一种是初始就在循环内;另外我猜想了所有循环节的最小公倍数应该是6;可是后面的操作我却并没有理清,就是一些细节的地方没有想清,很明显是线段树的题目,也肯定是要利用到这个循环节,我也想了如果一个区间内有数字不包含在循环段里,我是不能用延迟标记的,我需要暴力修改,最多6次,最多几次就进入了循环节;但是关键段区间都是在循环节内,我怎么整段区间修改为下一循环节,这里我没有理清楚,没办法,去牛客讨论区取经!看了别人的题解线段树节点记录的和有6个!我豁然开朗,区间保存循环和出现的6中结果,就可以利用延迟标记了!真的是思维上太呆了!


floyd判环算法(龟兔赛跑算法) ,可以辅助我们跑出所有的值自幂不超过6次后会进入循环过节,循环节的长度为1、2、3、6,最小公倍数为6.所以整段区间进入循环后,我们将仅需要将自幂n次变为一次赋值。
贴上:牛客网题解
下面附上个人写的龟兔赛跑算法的代码:

#include <iostream>
#include <stdio.h>
#include <algorithm>
#include <string.h>
#include <math.h>
#include <set>
#include <map>
#include <vector>
#define llt long long
using namespace std;

/******************************
*** floyd 判环算法(龟兔赛跑算法)

******************************/
const int mod = 2018;
int nxt[3000];
int floyd_circle(int s,int &pos,int &num){//初始点 pos 用来保存进入循环的位置 num记录s到pos的长度
    int pos_rab = s;
    int pos_tor = s;
    int cnt = 0; num = 0;
    do{
        //兔1s跑两步
        for(int i=1;i<=2;++i)
          if(nxt[pos_rab]==-1){pos=pos_rab;num++;return -1;}//无环,终点为pos
          else pos_rab = nxt[pos_rab];

        //乌龟1s跑1步
        pos_tor = nxt[pos_tor];
    }while(pos_rab!=pos_tor);

    //相遇了
    int k = pos_rab;//记录相遇位置
    pos_tor = s;
    while(pos_rab!=pos_tor){
        pos_rab = nxt[pos_rab];
        pos_tor = nxt[pos_tor];
        cnt++;
    }
    num = cnt; cnt = 0; pos = pos_rab;
    /*求循环节的长度*/
    do{
        cnt++;
        pos_rab = nxt[pos_rab];
    }while(pos_rab!=pos);
    return cnt;
}

int main(){
    //建边
    for(int i=0;i<mod;++i)
         nxt[i] = i*i%mod;

    for(int i=0;i<mod;++i){
        int pos,num;
        int k = floyd_circle(i,pos,num);
        if(k!=-1){
            printf("%d %d %d %d\n",i,pos,num,k);
`
       }else printf("%d %d %d\n",i,pos,num);
    }

}

接下来就是线段树的事了!我手写代码错了两个地方,调了一个晚上,代码能力太差·

#include <iostream>
#include <stdio.h>
#include <string.h>
#include <algorithm>
#include <set>
#define llt long long
#define lson (rt<<1)
#define rson ((rt<<1)+1)
using namespace std;

const int N = 50000 + 777;
const int mod = 2018;
bool circle[mod];

bool check(int x){
    int pre = x;
    for(int i=0;i<6;++i)
        x = x*x%mod;
    return x==pre;
}
struct stnode{
    int l,r,idx;
    bool in_circle;
    llt sum[6];
}p[N<<2];

llt A[N];
char s[3];
void update_top(int rt){
    if(p[lson].in_circle&&p[rson].in_circle){
        p[rt].in_circle = true;
        for(int i=0;i<6;++i)
            p[rt].sum[i] = p[lson].sum[i] + p[rson].sum[i];
    }else{
        p[rt].in_circle = false;
        for(int i=0;i<1;++i)
            p[rt].sum[i] = p[lson].sum[i] + p[rson].sum[i];
    }
}

void update(int rt,int val){
    int tmp[8];
    for(int i=0;i<6;++i)
        tmp[i] = p[rt].sum[(i+val)%6];
    for(int i=0;i<6;++i)
        p[rt].sum[i] = tmp[i];
    p[rt].idx +=val;//延迟标记写val,不要写成1
    p[rt].idx %=6;
}
void update_down(int rt){
    int val = p[rt].idx;
    if(val==0) return ;
    //将延迟回馈给左儿子
    update(lson,val);
    //将延迟回馈给右儿子
    update(rson,val);
    p[rt].idx = 0;
}
void mkStree(int rt,int l,int r){
    p[rt].l = l;
    p[rt].r = r;
    p[rt].idx = 0;
    p[rt].in_circle = false;
    memset(p[rt].sum,0,sizeof(p[rt].sum));
    if(l==r){
        p[rt].sum[0] = A[l];
        if(circle[A[l]]){
            p[rt].in_circle = true;
            for(int i=1;i<6;++i)
                p[rt].sum[i] = p[rt].sum[i-1]*p[rt].sum[i-1]%mod;
        }else p[rt].in_circle = false;

        return;
    }

    int mid = (l+r)>>1;
    mkStree(lson,l,mid);
    mkStree(rson,mid+1,r);
    update_top(rt);
}

void modify(int rt,int l,int r){
    if(l<=p[rt].l&&p[rt].r<=r&&p[rt].in_circle){//必须在环内
        update(rt,1);
        return ;
    }
    if(p[rt].l == p[rt].r){//没有在环内
        p[rt].sum[0] = p[rt].sum[0]*p[rt].sum[0]%mod;
        if(circle[p[rt].sum[0]]){//如果经过这次幂后,成功进环
            p[rt].in_circle = true;
            for(int i=1;i<6;++i)
                p[rt].sum[i] = p[rt].sum[i-1]*p[rt].sum[i-1]%mod;
        }
        return ;
    }
    update_down(rt);
    int mid = (p[rt].l+p[rt].r)>>1;
    if(l<=mid) modify(lson,l,r);// 不要写成modify(lson,l,mid);
    if(mid<r)  modify(rson,l,r);
    update_top(rt);
}

llt query(int rt,int l,int r){
    if(l<=p[rt].l&&p[rt].r<=r) return p[rt].sum[0];
    update_down(rt);
    int mid = (p[rt].l+p[rt].r)>>1;
    llt ans = 0;
    if(l<=mid) ans += query(lson,l,r);
    if(mid<r)  ans += query(rson,l,r);
    return ans;
}


int main(){
    for(int i=0;i<mod;++i) circle[i] = check(i);
    int cas;
    scanf("%d",&cas);
    for(int i=1;i<=cas;++i){
        int n,m;
        scanf("%d",&n);
        for(int i=1;i<=n;++i) scanf("%lld",&A[i]);
        mkStree(1,1,n);
        scanf("%d",&m);
        printf("Case #%d:\n",i);
        while(m--){
            int l,r;
            scanf("%s%d%d",s,&l,&r);
            if(s[0]=='C') modify(1,l,r);
            else printf("%lld\n",query(1,l,r));
        }
    }


    return 0;
}
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值