ZOJ4009

题意:

给出n个数字,有两种操作:

:将变成

:求模99971的结果。

思路:

打表可以发现,在该模数下,每48个数字会有循环节。所以开48棵线段树,在v[0]里放当前值。

对于修改操作,进行左移就可以了。

不清楚的话可以自己模拟一下。

因为刚开始,所以一开始就要模99971。

这题有点卡常,虽然跑过了但是太慢了,用个读入挂比较好。

代码:

#define MOD 99971
typedef long long ll;
struct node
{
    int left,right;
    int f;
    ll v[50];
}node[100010<<2];
ll temp1[50],temp2[50];
ll ans;
void pushup(int id)
{
    for(int i=0;i<48;i++)
        node[id].v[i]=(node[id<<1].v[i]+node[id<<1|1].v[i])%MOD;
}
void build(int l,int r,int id)
{
    node[id].left=l;node[id].right=r;
    node[id].f=0;
    if(l==r)
    {
        scanf("%lld",&node[id].v[0]);
        node[id].v[0]%=MOD;
        for(int i=1;i<48;i++)
            node[id].v[i]=node[id].v[i-1]*node[id].v[i-1]*node[id].v[i-1]%MOD;
        return;
    }
    int m=(l+r)>>1;
    build(l,m,id<<1);
    build(m+1,r,id<<1|1);
    pushup(id);
}
void pushdown(int id)
{
    int c=node[id].f;
    node[id<<1].f=(node[id<<1].f+c)%48;
    node[id<<1|1].f=(node[id<<1|1].f+c)%48;
    for(int i=0;i<48;i++)
    {
        temp1[i]=node[id<<1].v[i];
        temp2[i]=node[id<<1|1].v[i];
    }
    for(int i=0;i<48;i++)
    {
        node[id<<1].v[i]=temp1[(i+c)%48];
        node[id<<1|1].v[i]=temp2[(i+c)%48];
    }
    node[id].f=0;
}
void update(int L,int R,int id)
{
    int l=node[id].left,r=node[id].right;
    if(l>=L&&r<=R)
    {
        node[id].f=(node[id].f+1)%48;
        for(int i=0;i<48;i++)
            temp1[i]=node[id].v[i];
        for(int i=0;i<48;i++)
            node[id].v[i]=temp1[(i+1)%48];
        return;
    }
    if(node[id].f)pushdown(id);
    int m=(l+r)>>1;
    if(L<=m)update(L,R,id<<1);
    if(R>m)update(L,R,id<<1|1);
    pushup(id);
}
void query(int L,int R,int id)
{
    int l=node[id].left,r=node[id].right;
    if(l>=L&&r<=R)
    {
        ans=(ans+node[id].v[0])%MOD;
        return;
    }
    if(node[id].f)pushdown(id);
    int m=(l+r)>>1;
    if(L<=m)query(L,R,id<<1);
    if(R>m)query(L,R,id<<1|1);
}
int main()
{
    int t,n,q,op,x,y;
    scanf("%d",&t);
    while(t--)
    {
        scanf("%d%d",&n,&q);
        build(1,n,1);
        while(q--)
        {
            scanf("%d%d%d",&op,&x,&y);
            if(op==1)
                update(x,y,1);
            else if(op==2)
            {
                ans=0;
                query(x,y,1);
                printf("%lld\n",ans);
            }
        }
    }
    return 0;
}
上海大都会赛H

题意:

给出n个数字,有两种操作:

:将变成

:求的结果(不用取模)。

思路:

打表可知循环节大概是在变换4次以后,且每个循环节的长度为6。

所以这里就需要一个标记circle来记录该区间是否进入了循环节,如果进入了循环节,就像上题那样的做法,如果没有进入,则单点更新,并且判断更新后是否进入循环节。

上传标记的时候,只有左儿子和右儿子都在区间内的时候,将6个v[i]更新,否则就只更新v[0]。

我query的时候标记没有下放…debug了好久…一写长的代码就要debug好久…

代码:

typedef long long ll;
struct node
{
    int left,right;
    bool cir;
    int f;
    int v[10];
}node[50010<<2];
int temp1[10],temp2[10];
ll ans;
bool judge(int cur)
{
    int x=cur;
    for(int i=0;i<6;i++)
    {
        x=x*x%2018;
        if(x==cur)return true;
    }
    return false;
}
void build(int l,int r,int id)
{
    node[id].left=l;node[id].right=r;
    node[id].cir=false;node[id].f=0;
    if(l==r)
    {
        scanf("%d",&node[id].v[0]);
        return;
    }
    int m=(l+r)>>1;
    build(l,m,id<<1);
    build(m+1,r,id<<1|1);
    node[id].v[0]=(node[id<<1].v[0]+node[id<<1|1].v[0]);
}
void pushdown(int id)
{
    int c=node[id].f;
    node[id<<1].f=(node[id<<1].f+c)%6;
    node[id<<1|1].f=(node[id<<1|1].f+c)%6;
    for(int i=0;i<6;i++)
    {
        temp1[i]=node[id<<1].v[i];
        temp2[i]=node[id<<1|1].v[i];
    }
    for(int i=0;i<6;i++)
    {
        node[id<<1].v[i]=temp1[(i+c)%6];
        node[id<<1|1].v[i]=temp2[(i+c)%6];
    }
    node[id].f=0;
}
void update(int L,int R,int id)
{
    int l=node[id].left,r=node[id].right;
    if(l>=L&&r<=R&&node[id].cir)//循环内
    {
        node[id].f=(node[id].f+1)%6;
        for(int i=0;i<6;i++)
            temp1[i]=node[id].v[i];
        for(int i=0;i<6;i++)
            node[id].v[i]=temp1[(i+1)%6];
        return;
    }
    if(l==r)//循环外
    {
        node[id].v[0]=node[id].v[0]*node[id].v[0]%2018;
        if(judge(node[id].v[0]))
        {
            node[id].cir=true;
            for(int i=1;i<6;i++)
            node[id].v[i]=node[id].v[i-1]*node[id].v[i-1]%2018;
        }
        return;
    }
    if(node[id].f)pushdown(id);
    int m=(l+r)>>1;
    if(L<=m)update(L,R,id<<1);
    if(R>m)update(L,R,id<<1|1);
    if(node[id<<1].cir&&node[id<<1|1].cir)
    {
        node[id].cir=true;
        for(int i=0;i<6;i++)
            node[id].v[i]=node[id<<1].v[i]+node[id<<1|1].v[i];
    }
    else
    {
        node[id].cir=false;
        node[id].v[0]=node[id<<1].v[0]+node[id<<1|1].v[0];
    }
}
void query(int L,int R,int id)
{
    int l=node[id].left,r=node[id].right;
    if(l>=L&&r<=R)
    {
        ans+=1ll*node[id].v[0];
        return;
    }
    if(node[id].f)pushdown(id);//不要忘了下放
    int m=(l+r)>>1;
    if(L<=m)query(L,R,id<<1);
    if(R>m)query(L,R,id<<1|1);
}
int main()
{
    int t,n,q,x,y;
    char s[5];
    scanf("%d",&t);
    for(int kase=1;kase<=t;kase++)
    {
        printf("Case #%d:\n",kase);
        scanf("%d",&n);
        build(1,n,1);
        scanf("%d",&q);
        while(q--)
        {
            scanf("%s%d%d",s,&x,&y);
            if(s[0]=='C')
                update(x,y,1);
            else if(s[0]=='Q')
            {
                ans=0;
                query(x,y,1);
                printf("%lld\n",ans);
            }
        }
    }
    return 0;
}

本博客所有文章除特别声明外,均采用 CC BY-SA 4.0 协议 ,转载请注明出处!

HDU6415 推导/DP Previous
HDU5884 二分+多叉哈夫曼树+单调队列 Next