HDU 4578 Transformation --线段树,好题

题意: 给一个序列,初始全为0,然后有4种操作:

1. 给区间[L,R]所有值+c

2.给区间[L,R]所有值乘c

3.设置区间[L,R]所有值为c

4.查询[L,R]的p次方和(1<=p<=3)

解法: 线段树,维护三个标记,addmark,mulmark,setmark分别表示3种更新,然后p[1],p[2],p[3]分别表示该节点的1,2,3次方和。标记传递顺序setmark一定是第一个,因为setmark可以使mulmark,addmark都失效还原,mulmark和addmark的顺序倒是无所谓。

这题写了半个比赛时间,写的很迷,还是没写出来, 后来看了别人写的,才知道自己很多地方都没更新好甚至没更新。这题算是一道线段树的综合题了,混合了几种常见的更新,对线段树的整体把握很有帮助。

比如:

1.如果setmark有值,那么addmark,mulmark全部要还原。

2.如果mulmark有值,那么addmark也要更新,更新为addmark*mulmark

就是这两点一直没考虑到,WA了好久。

在addmark下传过程中更新p[1],p[2],p[3]的方法如下:

比如 a^3 -> (a+c)^3 的过程:  (a+c)^3 = a^3 + c^3 + 3a*c^2 + 3*a^2*c, a是变量, 所以提取出c,那么p[3]可以由p[1],p[2]推出,p[2]可以由p[1]推出。

即 p[3] = p[3] + c^3 + 3*c^2*p[1] + 3*c*p[2] .

p[2]同理可以由p[1]推出。

然后每次都做一下pushdown,就可以得出正确答案。

当时像优化一下,少做几次pushdown,即这次的操作类型与上次一样就不pushdown,结果那样就会出现问题,还不如每次都pushdown呢。

代码:

#include <iostream>
#include <cstdio>
#include <cstring>
#include <cstdlib>
#include <cmath>
#include <algorithm>
#define Mod 10007
#define SMod 10007
using namespace std;
#define N 100017

struct node{
    int p[4];
    int setmark,addmark,mulmark;
}tree[4*N];

void pushup(int rt){
    for(int i=1;i<=3;i++) tree[rt].p[i] = (tree[2*rt].p[i] + tree[2*rt+1].p[i])%SMod;
}

void pushdown(int l,int r,int rt)
{
    int mid = (l+r)/2;
    if(tree[rt].setmark)
    {
        int mk = tree[rt].setmark;
        tree[rt<<1].setmark = tree[rt<<1|1].setmark = mk;
        tree[rt<<1].addmark = tree[rt<<1|1].addmark = 0;
        tree[rt<<1].mulmark = tree[rt<<1|1].mulmark = 1;

        tree[rt<<1].p[1] = (mid-l+1)%Mod*mk%SMod;
        tree[rt<<1|1].p[1] = (r-mid)%Mod*mk%SMod;

        tree[rt<<1].p[2] = (mid-l+1)%Mod*mk%SMod*mk%SMod;
        tree[rt<<1|1].p[2] = (r-mid)%Mod*mk%SMod*mk%SMod;

        tree[rt<<1].p[3] = (mid-l+1)%Mod*mk%SMod*mk%SMod*mk%SMod;
        tree[rt<<1|1].p[3] = (r-mid)%Mod*mk%SMod*mk%SMod*mk%SMod;
        tree[rt].setmark = 0;
    }
    if(tree[rt].mulmark != 1)
    {
        int mk = tree[rt].mulmark;
        tree[rt<<1].mulmark *= mk, tree[rt<<1].mulmark%=SMod;
        tree[rt<<1|1].mulmark *= mk, tree[rt<<1|1].mulmark%=SMod;

        tree[rt<<1].addmark = tree[rt<<1].addmark%SMod*mk%SMod;
        tree[rt<<1|1].addmark = tree[rt<<1|1].addmark%SMod*mk%SMod;

        tree[rt<<1].p[1] = tree[rt<<1].p[1]%Mod*mk%SMod;
        tree[rt<<1|1].p[1] = tree[rt<<1|1].p[1]%Mod*mk%SMod;

        tree[rt<<1].p[2] = tree[rt<<1].p[2]%Mod*mk%SMod*mk%SMod;
        tree[rt<<1|1].p[2] = tree[rt<<1|1].p[2]%Mod*mk%SMod*mk%SMod;

        tree[rt<<1].p[3] = tree[rt<<1].p[3]%Mod*mk%SMod*mk%SMod*mk%SMod;
        tree[rt<<1|1].p[3] = tree[rt<<1|1].p[3]%Mod*mk%SMod*mk%SMod*mk%SMod;
        tree[rt].mulmark = 1;
    }
    if(tree[rt].addmark)
    {
        int mk = tree[rt].addmark;
        tree[rt<<1].addmark += mk, tree[rt<<1].addmark%SMod;
        tree[rt<<1|1].addmark += mk, tree[rt<<1|1].addmark%SMod;

        tree[rt<<1].p[3] = (tree[rt<<1].p[3]%Mod + (mid-l+1)%Mod*mk%SMod*mk%SMod*mk%SMod + 3*mk%Mod*tree[rt<<1].p[2]%SMod + 3*mk%SMod*mk%SMod*tree[rt<<1].p[1]%SMod)%SMod;
        tree[rt<<1|1].p[3] = (tree[rt<<1|1].p[3]%Mod + (r-mid)%Mod*mk%SMod*mk%SMod*mk%SMod + 3*mk%Mod*tree[rt<<1|1].p[2]%SMod + 3*mk%SMod*mk%SMod*tree[rt<<1|1].p[1]%SMod)%SMod;

        tree[rt<<1].p[2] = (tree[rt<<1].p[2]%Mod + (mid-l+1)%Mod*mk%SMod*mk%SMod + 2*mk%Mod*tree[rt<<1].p[1]%SMod)%SMod;
        tree[rt<<1|1].p[2] = (tree[rt<<1|1].p[2]%Mod + (r-mid)%Mod*mk%SMod*mk%SMod + 2*mk%Mod*tree[rt<<1|1].p[1]%SMod)%SMod;

        tree[rt<<1].p[1] = (tree[rt<<1].p[1]%Mod + (mid-l+1)%Mod*mk%SMod)%SMod;
        tree[rt<<1|1].p[1] = (tree[rt<<1|1].p[1]%Mod + (r-mid)%Mod*mk%SMod)%SMod;
        tree[rt].addmark = 0;
    }
}

void build(int l,int r,int rt)
{
    tree[rt].setmark = tree[rt].addmark = 0;
    tree[rt].mulmark = 1;
    memset(tree[rt].p,0,sizeof(tree[rt].p));
    if(l == r) return;
    int mid = (l+r)/2;
    build(l,mid,rt<<1);
    build(mid+1,r,rt<<1|1);
}

void update(int l,int r,int aa,int bb,int tag,int val,int rt)  //tag = 3 : set  2:mul 1: add
{
    if(aa <= l && bb >= r)
    {
        if(tag == 3)
        {
            tree[rt].p[1] = (r-l+1)%Mod*val%SMod;
            tree[rt].p[2] = (r-l+1)%Mod*val%SMod*val%SMod;
            tree[rt].p[3] = (r-l+1)%Mod*val%SMod*val%SMod*val%SMod;
            tree[rt].setmark = val;
            tree[rt].addmark = 0;
            tree[rt].mulmark = 1;
        }
        else if(tag == 2)
        {
            tree[rt].p[1] = tree[rt].p[1]%SMod*val%SMod;
            tree[rt].p[2] = tree[rt].p[2]%SMod*val%SMod*val%SMod;
            tree[rt].p[3] = tree[rt].p[3]%SMod*val%SMod*val%SMod*val%SMod;
            tree[rt].mulmark = tree[rt].mulmark%SMod*val%SMod;
            tree[rt].addmark = tree[rt].addmark%SMod*val%SMod;
        }
        else if(tag == 1)
        {
            tree[rt].p[3] = (tree[rt].p[3]%SMod + (r-l+1)%SMod*val%SMod*val%SMod*val%SMod + 3*val%SMod*tree[rt].p[2]%SMod + 3*val%SMod*val%SMod*tree[rt].p[1]%SMod)%SMod;
            tree[rt].p[2] = (tree[rt].p[2]%SMod + (r-l+1)%SMod*val%SMod*val%SMod + 2*val%SMod*tree[rt].p[1]%SMod)%SMod;
            tree[rt].p[1] = (tree[rt].p[1]%SMod + (r-l+1)%SMod*val%SMod)%SMod;
            tree[rt].addmark = (tree[rt].addmark+val)%SMod;
        }
        return;
    }
    int mid = (l+r)/2;
    pushdown(l,r,rt);
    if(aa <= mid) update(l,mid,aa,bb,tag,val,rt<<1);
    if(bb > mid)  update(mid+1,r,aa,bb,tag,val,rt<<1|1);
    pushup(rt);
}

int query(int l,int r,int aa,int bb,int i,int rt)
{
    if(aa > r || bb < l) return 0;
    if(aa <= l && bb >= r)
        return tree[rt].p[i];
    pushdown(l,r,rt);
    int res = 0;
    int mid = (l+r)/2;
    return (query(l,mid,aa,bb,i,rt<<1)%SMod+query(mid+1,r,aa,bb,i,rt<<1|1)%SMod)%SMod;
}

int main()
{
    int n,m,i,j,op,x,y,c;
    while(scanf("%d%d",&n,&m)!=EOF && n+m)
    {
        build(1,n,1);
        while(m--)
        {
            scanf("%d%d%d%d",&op,&x,&y,&c);
            if(op != 4) update(1,n,x,y,op,c,1);
            else        printf("%d\n",query(1,n,x,y,c,1)%SMod);
        }
    }
    return 0;
}

时间: 2024-10-14 08:46:55

HDU 4578 Transformation --线段树,好题的相关文章

hdu 4578 Transformation(线段树)

Transformation Time Limit: 15000/8000 MS (Java/Others)    Memory Limit: 65535/65536 K (Java/Others)Total Submission(s): 3084    Accepted Submission(s): 749 Problem Description Yuanfang is puzzled with the question below: There are n integers, a1, a2,

HDU 4893 线段树裸题

Wow! Such Sequence! Time Limit: 10000/5000 MS (Java/Others)    Memory Limit: 65536/65536 K (Java/Others) Total Submission(s): 2512    Accepted Submission(s): 751 Problem Description Recently, Doge got a funny birthday present from his new friend, Pro

HDU 4027 Can you answer these queries? 线段树裸题

题意: 给定2个操作 0.把区间的每个数sqrt 2.求和 因为每个数的sqrt次数很少,所以直接更新到底,用个标记表示是否更新完全(即区间内的数字只有0,1就不用再更新了) #include<stdio.h> #include<iostream> #include<algorithm> #include<vector> #include<cmath> #include<queue> #include<set> #incl

hdu 4578 Transformation

http://acm.hdu.edu.cn/showproblem.php?pid=4578 又做了一道好题.. 有三种操作: 1 a b c [a,b]加上c 2 a b c [a,b]乘上c 3 a b c [a,b]变为c 4 a b c 求[a,b]的c次方和(1<=c<=3) 这题首先需要解决的第一个问题是加上或乘上一个数对这个区间的c次方和分别产生什么改变,很简单,一化简就能得到. 第二个问题是当一段区间上既有乘又有加的lazy时应该怎么向下推送,因为一段区间上只能有一个lazy,

HDU 1542 Atlantis 线段树+离散化+扫描线

题意:给出一些矩形的最上角坐标和右下角坐标,求这些矩形的面积并. NotOnlySuccess 线段树专辑中扫描线模板题,弱智的我对着大大的代码看了一下午才搞懂. 具体见思路见注释=.= #include <cstdio> #include <cstring> #include <algorithm> #include <vector> #define lson rt<<1,l,mid #define rson rt<<1|1,mid

HDU 1542 Atlantis(线段树扫描线)

http://acm.hdu.edu.cn/showproblem.php?pid=1542 Atlantis Time Limit: 2000/1000 MS (Java/Others)    Memory Limit: 65536/32768 K (Java/Others) Total Submission(s): 6788    Accepted Submission(s): 2970 Problem Description There are several ancient Greek

【整理】线段树30题

1,poj 1151 Atlantis: 求矩形面积并. 2,poj 1177 Picture: 求矩形轮廓的周长. 3,poj 1389 Area of Simple Polygons :同第一题. 4,poj 1823 Hotel :线段树线段的插入删除求线段树中最长的线段长度 5,poj 2104 K-th Number:线段树维护归并排序树+三次二分查找   (区间第k大 ,主席树也行,前者可以练习试一下). 6,poj 2155 Matrix :求二维平面的矩形信息,二维线段树,或者二

HDU 4085 斯坦纳树模板题

Dig The Wells Time Limit: 6000/2000 MS (Java/Others)    Memory Limit: 65536/32768 K (Java/Others) Total Submission(s): 971    Accepted Submission(s): 416 Problem Description You may all know the famous story "Three monks". Recently they find som

[POJ2104] 区间第k大数 [区间第k大数,可持久化线段树模板题]

可持久化线段树模板题. #include <iostream> #include <algorithm> #include <cstdio> #include <cstdlib> #include <cstring> #include <cmath> #include <ctime> #include <vector> using namespace std; int n,q,tot,a[110000]; in