JZOJsenior1384. Alice的游戏

题目描述

Description

  Alice又想到一个游戏:N个数每个数都在0到9之间,可以对每一个数进行加1操作,但这个加1比较特别,0-8加1后会相应变成1-9,但9加1后会变成0,给出N个数,进行M次操作,每次操作都会给出两个整数A和B(1<=A<=B<=N),要求输出第A个数到第B个数的和,然后把A到B之间的每一个数进行一次加1操作。
  你能帮助Alice吗?

Input

  输入文件第一行包含两个整数N和M(1<=N<=250000,1<=M<=100000);
  第二行N个数字,每个数字都在0到9之间,中间没有空格,表示初始值。
  接下来M行,每行包含两个整数A和B(1<=A<=B<=N)

Output

  输出M行,每行表示对应区间的和,注意是先计算后操作。

Sample Input

输入1:
4 3
1234
1 4
1 4
1 4

输入2:
4 4
1234
1 1
1 2
1 3
1 4

输入3:
7 5
9081337
1 3
3 7
1 3
3 7
1 3

Sample Output

输出1:
10
14
18

输出2:
1
4
9
16

输出3:
17
23
1
19
5

Data Constraint

Hint

【数据范围】
  30%的数据N,M<=1000


思路

这题的正解是线段树,不过比普通线段树难搞很多
这里的线段树每一个点维护一个0~9的序列,表示0~9出现了多少次
每次修改时就把0~8改到1~9的次数,9改成0的次数,然后再下传标记
由于转x次相当于转x%10次,所以这里相当于一个小优化

打起来比较麻烦,而且还有一个大坑——
pascal卡常!思路完全对!答案是对的!超了几百+ms!woc……mdzz……RNMMP……

于是怒改c++强行开O3切掉,只不过code极其难看……


pascal code(70分)

type
        point=record
                sum,flag,p:longint;
                a:array[0..9]of longint;
        end;
var
        tree:array[0..1234567]of point;
        n,m,i,j,x,y:longint;
        ch:char;
procedure update(x:longint);
var
        i,k:longint;
begin
        tree[x].p:=tree[x].p mod 10;
        for i:=1 to tree[x].p do
        begin
                k:=tree[x].a[9];
                for j:=9 downto 1 do
                        tree[x].a[j]:=tree[x].a[j-1];
                tree[x].a[0]:=k;
        end;
        tree[x].sum:=0;
        for i:=0 to 9 do
                inc(tree[x].sum,tree[x].a[i]*i);
        tree[x].p:=0;
end;
procedure downdata(x:longint);
var
        i,j,k:longint;
begin
        inc(tree[2*x].flag,tree[x].flag);
        inc(tree[2*x+1].flag,tree[x].flag);
        inc(tree[2*x+1].p,tree[x].flag);
        inc(tree[2*x].p,tree[x].flag);
        tree[x].flag:=0;
        update(x*2);
        update(x*2+1);
        update(x);
        tree[x].flag:=0;
end;
procedure maketree(x,l,r:longint);
var
        i,mid:longint;
begin
        if l=r then
        begin
                read(ch);
                tree[x].a[ord(ch)-48]:=1;
                tree[x].sum:=ord(ch)-48;
                exit;
        end;
        mid:=(l+r)shr 1;
        maketree(2*x,l,mid);
        maketree(2*x+1,mid+1,r);
        tree[x].sum:=tree[2*x].sum+tree[2*x+1].sum;
        for i:=0 to 9 do
                tree[x].a[i]:=tree[2*x].a[i]+tree[2*x+1].a[i];
end;
function solve(x,y,t,l,r:longint):longint;
var
        mid:longint;
begin
        downdata(t);
        if (l>=x)and(r<=y)then
        begin
                exit(tree[t].sum);
        end;
        mid:=(l+r)shr 1;
        if mid>=y then exit(solve(x,y,2*t,l,mid))
        else if midthen exit(solve(x,y,2*t+1,mid+1,r))
        else exit(solve(x,y,2*t,l,mid)+solve(x,y,2*t+1,mid+1,r));
end;
procedure change(t,l,r,x,y:longint);
var
        i,j,k,mid:longint;
begin
        downdata(t);
        if (l>=x)and(r<=y)then
        begin
                inc(tree[t].flag);
                inc(tree[t].p);
                downdata(t);
                exit;
        end;
        mid:=(l+r)shr 1;
        if y<=mid then change(2*t,l,mid,x,y)
        else if x>mid then change(2*t+1,mid+1,r,x,y)
        else
        begin
                change(2*t,l,mid,x,y);
                change(2*t+1,mid+1,r,x,y);
        end;
        tree[t].sum:=tree[t*2].sum+tree[t*2+1].sum;
        for i:=0 to 9 do
                tree[t].a[i]:=tree[2*t].a[i]+tree[2*t+1].a[i];
end;
begin
        readln(n,m);
        maketree(1,1,n);
        for i:=1 to m do
        begin
                readln(x,y);
                writeln(solve(x,y,1,1,n));
                change(1,1,n,x,y);
        end;
end.

c++ code(AC)

#pragma GCC opitmize("O3")
#pragma G++ opitmize("O3")
#include

using namespace std;

int f[1020251],a[264451],s[1020251],p[1020251],val[1020251][10];
int n,m,t,l,r,c,nn;
char str[264451];

void tsum(int v)
{
    f[v]=f[v*2]+f[v*2+1];
    for(int i=0;i<=9;i++)
    {
        val[v][i]=val[v*2][i]+val[v*2+1][i];
    }
}

void maketree(int v,int l,int r)
{
    if(l==r)
    {
        f[v]=a[l];
        val[v][a[l]]++;
        return;
    }
    int mid=(l+r)>>1;
    maketree(v*2,l,mid);
    maketree(v*2+1,mid+1,r);
    tsum(v);
}

void update(int x)
{
    if(x>=nn)return;
    int o[10];
    for(int i=0;i<10;i++)o[(i+p[x])%10]=val[x][i];
    memcpy(val[x],o,sizeof(o)); 
    f[x]=0;
    for(int i=0;i<10;i++)
        f[x]+=val[x][i]*i;
    p[x]=0;
}

void doit(int v)
{
    s[v*2]+=s[v],s[v*2+1]+=s[v],p[v*2]+=s[v],p[v*2+1]+=s[v],s[v]=0;
    update(v*2);
    update(v*2+1);
    update(v);
}

void change(int v,int l,int r,int x,int y)
{
    if(l==x && r==y)
    {
        s[v]++;
        p[v]++;
        update(v);
        return;
    }
    doit(v);
    int mid=(l+r)/2;
    if(y<=mid)change(v*2,l,mid,x,y);
    if(x>mid)change(v*2+1,mid+1,r,x,y);
    if(!(y<=mid||x>mid)){
        change(v*2,l,mid,x,mid);
        change(v*2+1,mid+1,r,mid+1,y);
    }
    tsum(v);
}

int find(int v,int l,int r,int x,int y)
{
    if(l==x&&r==y)
    {
        return f[v];    
    }
    doit(v);
    int mid=(l+r)/2;
    if(x>mid)return find(v*2+1,mid+1,r,x,y);
    if(y<=mid)return find(v*2,l,mid,x,y);
    return find(v*2,l,mid,x,mid)+find(v*2+1,mid+1,r,mid+1,y);
}

int main()
{
    scanf("%d%d%s",&n,&m,str+1);
    for(int i=1;i<=n;i++)a[i]=int(str[i])-48;
    t=n,nn=1;
    for(;t>1;t=(t+1)/2,nn*=2);
    nn*=2;
    maketree(1,1,n);
    while(m--)
    {
        scanf("%d%d",&l,&r);
        printf("%d\n",find(1,1,n,l,r));
        change(1,1,n,l,r);
    }
}

你可能感兴趣的:(线段树,模拟赛)