【Nowcoder】2018 ACM-ICPC 上海大都会赛 A Simple Problem with Integers (线段树 思维)

题目大意

对于一个序列,进行以下两种操作:1)将区间 [li,ri] 内的数字平方后 mod 2018,2)求不取模的平方和。


解题思路

由于模数很小,所以可以从这里下手。打一个表之后可以发现,每个数都将在进行几次 mod 2018 意义下的平方运算之后进入一个长度很小的循环节,且所有循环节长度的 lcm 为 6。而且可以发现,每个数字经过不超过 5 次运算都可以进入循环节,所以在某个数字进入循环节之前,每次都可以对其进行暴力修改。

对于线段树的每个结点,维护以下几个量:1)sum:区间和;2)cir[]:该结点的循环节内各位表示的数;3)len:该结点的循环节长度;4)pos:该结点当前在循环节的位置;5)in:该结点是否在循环节;6)tag:加标记。


代码

忘记初始化了,比赛时候wa了一发好难受啊。

#include <bits/stdc++.h>
using namespace std;

inline int read() {
    register int val=0, sign=1; char ch;
    while(~(ch=getchar()) && (ch<'0' || ch>'9') && ch!='-'); ch=='-'?sign=-1:val=ch-'0';
    while(~(ch=getchar()) && (ch>='0' && ch<='9')) val=(val<<1)+(val<<3)+ch-'0';
    return val*sign;
}

const int maxn=int(1e5)+111, p=2018, maxp=2333;
int n,m;
int a[maxn];
int nxt[maxp], vis[maxp], inc[maxp];

int sum[maxn<<2];
int len[maxn<<2], cir[maxn<<2][10];
int pos[maxn<<2], in[maxn<<2], tag[maxn<<2];

inline int gcd(int a,int b) {return b?gcd(b,a%b):a;}
inline int lcm(int a,int b) {return a*b/gcd(a,b);}

void init(int k,int v) {
    sum[k]=v, in[k]=inc[v];
    if(in[k]) {
        len[k]=1, cir[k][pos[k]=0]=v;
        for(int i=nxt[v];i!=v;i=nxt[i])
            cir[k][len[k]++]=i;
    }
    return;
}

#define mid ((l+r)>>1)
#define ls(k) ((k)<<1)
#define rs(k) (ls(k)|1)

void update(int k) {
    sum[k]=sum[ls(k)]+sum[rs(k)];
    in[k]=in[ls(k)]&in[rs(k)];
    if(in[k]) {
        len[k]=lcm(len[ls(k)],len[rs(k)]), pos[k]=0;
        assert(len[k]);
        int tx=pos[ls(k)], ty=pos[rs(k)];
        for(int i=0;i<len[k];i++) {
            cir[k][i]=cir[ls(k)][tx++]+cir[rs(k)][ty++];
            if(tx==len[ls(k)]) tx=0;
            if(ty==len[rs(k)]) ty=0;
        }
    }
    return;
}

void add(int k,int i) {
    tag[k]+=i, pos[k]+=i;
    assert(len[k]);
    while(len[k] && pos[k]>=len[k]) pos[k]%=len[k];
    sum[k]=cir[k][pos[k]];
}
void pushdown(int k) {
    if(!tag[k]) return;
    add(ls(k),tag[k]), add(rs(k),tag[k]);
    tag[k]=0;
    return;
}

void build(int k,int l,int r) {
    sum[k]=in[k]=tag[k]=pos[k]=len[k]=0;
    if(l==r) {init(k,a[l]); return;}
    build(ls(k),l,mid), build(rs(k),mid+1,r);
    update(k);
    return;
}

void modify(int k,int l,int r,int x,int y) {
    if(x<=l && r<=y && in[k]) {add(k,1); return;}
    if(l==r) {init(k,nxt[sum[k]]); return;}
    pushdown(k);
    if(x<=mid) modify(ls(k),l,mid,x,y);
    if(y> mid) modify(rs(k),mid+1,r,x,y);
    update(k);
    return;
}

int query(int k,int l,int r,int x,int y) {
    if(x<=l && r<=y) return sum[k];
    if(tag[k]) pushdown(k);
    int res=0;
    if(x<=mid) res+=query(ls(k),l,mid,x,y);
    if(y> mid) res+=query(rs(k),mid+1,r,x,y);
    return res;
}

void work() {
    n=read();
    for(int i=1;i<=n;i++)
        a[i]=read();
    build(1,1,n);

    char op[10];
    m=read();
    while(m--) {
        scanf("%s",op);
        int l=read(), r=read();
        if(op[0]=='Q') printf("%d\n",query(1,1,n,l,r));
        else modify(1,1,n,l,r);
    }
}

int main() {
#ifndef ONLINE_JUDGE
    freopen("input0.txt","r",stdin);
    freopen("output.txt","w",stdout);
#endif
    for(int i=0;i<p;i++) inc[i]=1, nxt[i]=i*i%p;
    for(int i=0;i<p;i++) if(!vis[i]) {
        int y=i;
        while(!vis[y]) vis[y]=1, y=nxt[y];
        for(int x=i;x!=y;x=nxt[x]) inc[x]=0;
    }

    int T=read();
    for(int i=1;i<=T;++i) {
        printf("Case #%d:\n",i);
        work();
    }

    return 0;
}
©️2020 CSDN 皮肤主题: 技术黑板 设计师:CSDN官方博客 返回首页