HDU 4578 线段树玄学算法?

Transformation

题目链接

http://acm.hdu.edu.cn/showproblem.php?pid=4578

Problem Description

Yuanfang is puzzled with the question below:
There are n integers, a1, a2, …, an. The initial values of them are 0. There are four kinds of operations.
Operation 1: Add c to each number between ax and ay inclusive. In other words, do transformation ak<---ak+c, k = x,x+1,…,y.
Operation 2: Multiply c to each number between ax and ay inclusive. In other words, do transformation ak<---ak×c, k = x,x+1,…,y.
Operation 3: Change the numbers between ax and ay to c, inclusive. In other words, do transformation ak<---c, k = x,x+1,…,y.
Operation 4: Get the sum of p power among the numbers between ax and ay inclusive. In other words, get the result of axp+ax+1p+…+ay p.
Yuanfang has no idea of how to do it. So he wants to ask you to help him.

Input

There are no more than 10 test cases.
For each case, the first line contains two numbers n and m, meaning that there are n integers and m operations. 1 <= n, m <= 100,000.
Each the following m lines contains an operation. Operation 1 to 3 is in this format: "1 x y c" or "2 x y c" or "3 x y c". Operation 4 is in this format: "4 x y p". (1 <= x <= y <= n, 1 <= c <= 10,000, 1 <= p <= 3)
The input ends with 0 0.

Output

For each operation 4, output a single integer in one line representing the result. The answer may be quite large. You just need to calculate the remainder of the answer when divided by 10007.

Sample Input

    5 5
    3 3 5 7
    1 2 4 4
    4 1 5 2
    2 2 5 8
    4 3 5 3
    0 0

Sample Output

    307
    7489

题意

给你一个序列,支持四种操作

1.区间加法

2.区间乘法

3.区间减法

4.求和,平方和,立方和 即\(\large \sum_{i=l}^{r}{a_i^p}(1\le p\le 3)\)

题解

一开始看到这道题,觉得可以用数学公式搞搞,搞了半天确实搞出了个公式,用sum1,sum2,sum3分别存和,平方和,立方和,然后合并的时候再搞

搞。但是感觉很麻烦,于是先上网查了查正解是不是有什么巧妙的方法。但是看完网上题解,我才发现都是用的玄学复杂度。

于是我就愉快地也跟着各位大佬一样玄学操作啦。

具体操作:还是用线段树,遇到一段连续相同的区间就可以马上得到答案,其余部分直接暴力就行,我寻思着只要先把每个数都变得不一样然后求所有数的立方和,直接就暴了(别想那么多,这题纯属娱乐)。

代码

#include<bits/stdc++.h>
using namespace std;
#define ll long long
#define INF 0x7f7f7f7f
#define N 100050
#define mo 10007
ll n,m;
struct Node{ll l,r,lazy;};
struct segmentTree
{
    Node tr[N<<2];
    void push_up(ll x);
    void push_down(ll x);
    void bt(ll x,ll l,ll r);
    void add(ll x,ll l,ll r,ll tt);
    void multiply(ll x,ll l,ll r,ll tt);
    void cover(ll x,ll l,ll r,ll tt);
    ll query(ll x,ll l,ll r,ll tt);
}seg;
template<typename T>void read(T&x)
{
    ll k=0; char c=getchar();
    x=0;
    while(!isdigit(c)&&c!=EOF)k^=c=='-',c=getchar();
    if (c==EOF)exit(0);
    while(isdigit(c))x=x*10+c-'0',c=getchar();
    x=k?-x:x;
}
void read_char(char &c)
{while(!isalpha(c=getchar())&&c!=EOF);}
void segmentTree::push_up(ll x)
{
    if(tr[x].l==tr[x].r)return;
    Node &a=tr[x<<1],&b=tr[x<<1|1];
    if (a.lazy==b.lazy&&tr[x].lazy==-1)tr[x].lazy=a.lazy;
}
void segmentTree::push_down(ll x)
{
    if (tr[x].lazy==-1)return;
    tr[x<<1].lazy=tr[x].lazy;
    tr[x<<1|1].lazy=tr[x].lazy;
    tr[x].lazy=-1;
}
void segmentTree::bt(ll x,ll l,ll r)
{
    tr[x]=Node{l,r,0};
    if (l==r)return;
    ll mid=(l+r)>>1;
    bt(x<<1,l,mid);
    bt(x<<1|1,mid+1,r);
}
void segmentTree::add(ll x,ll l,ll r,ll tt)
{
    if (l<=tr[x].l&&tr[x].r<=r&&tr[x].lazy!=-1)
    {
        tr[x].lazy+=tt;
        tr[x].lazy%=mo;
        return;
    }
    ll mid=(tr[x].l+tr[x].r)>>1;
    push_down(x);
    if (l<=mid)add(x<<1,l,r,tt);
    if (mid<r)add(x<<1|1,l,r,tt);
    push_up(x);
}
void segmentTree::multiply(ll x,ll l,ll r,ll tt)
{
    if (l<=tr[x].l&&tr[x].r<=r&&tr[x].lazy!=-1)
    {
        tr[x].lazy*=tt;
        tr[x].lazy%=mo;
        return;
    }
    ll mid=(tr[x].l+tr[x].r)>>1;
    push_down(x);
    if (l<=mid)multiply(x<<1,l,r,tt);
    if (mid<r)multiply(x<<1|1,l,r,tt);
    push_up(x);
}
void segmentTree::cover(ll x,ll l,ll r,ll tt)
{
    if (l<=tr[x].l&&tr[x].r<=r&&tr[x].lazy!=-1)
    {
        tr[x].lazy=tt%mo;
        return;
    }
    ll mid=(tr[x].l+tr[x].r)>>1;
    push_down(x);
    if (l<=mid)cover(x<<1,l,r,tt);
    if (mid<r)cover(x<<1|1,l,r,tt);
    push_up(x);
}
ll segmentTree::query(ll x,ll l,ll r,ll tt)
{
    if (l<=tr[x].l&&tr[x].r<=r&&tr[x].lazy!=-1)
    {
        ll ans=1;
        for(ll i=1;i<=tt;i++)ans*=tr[x].lazy;
        ans*=(tr[x].r-tr[x].l+1);
        return ans%mo;
    }
    ll mid=(tr[x].l+tr[x].r)>>1,ans=0;
    push_down(x);
    if(l<=mid)ans+=query(x<<1,l,r,tt);
    if(mid<r)ans+=query(x<<1|1,l,r,tt);
    push_up(x);
    return ans%mo;
}
void work()
{
    read(n); read(m);
    if (n+m==0)exit(0);
    seg.bt(1,1,n);
    for(ll i=1;i<=m;i++)
    {
        ll id,x,y,tt;
        read(id); read(x); read(y); read(tt);
        if (id==1)seg.add(1,x,y,tt);
        if (id==2)seg.multiply(1,x,y,tt);
        if (id==3)seg.cover(1,x,y,tt);
        if (id==4)printf("%lld\n",seg.query(1,x,y,tt));
    }
}
int main()
{
#ifndef ONLINE_JUDGE
    freopen("aa.in","r",stdin);
#endif
    while(1)work();
}

原文地址:https://www.cnblogs.com/mmmqqdd/p/11241062.html

时间: 2024-10-15 01:07:10

HDU 4578 线段树玄学算法?的相关文章

HDU 4578 线段树区间更新(确定区间操作的优先级)

HDU 4578 线段树区间更新 操作有: 区间所有数add(c) 区间所有数mul(c) 区间所有数set(c) 查询有: 区间所有数的p次方和(p>= 1 && p <= 3) 关键是区间更新的三种操作的优先级的确定清楚set>mul>add 关键是:down和update中对区间的更新操作是一回事,可以写成函数方便编程 //#pragma warning (disable: 4786) //#pragma comment (linker, "/STA

HDU 4578(线段树

题目: 要求支持四种区间操作:1.区间加上一个数,2.区间乘上一个数,3.区间全部变成一个数,4.区间求和(要求支持平方和,立方和. 思路:这题有些恶心啊.....幸而交上去1A,不然我感觉真得难以debug.....注意下传标记时三种操作的顺序.(常数有点大...估计是多余的mod造成的... //priority_queue #pragma comment(linker, "/STACK:102400000,102400000") #include <iostream>

K - Transformation HDU - 4578 线段树经典题(好题)

题意:区间  加   变成定值 乘  区间查询:和 平方和 立方和 思路:超级超级超级麻烦的一道题  设3个Lazy 标记分别为  change 改变mul乘 add加  优先度change>mul>add因为改变了之后 前面的mul 和add都失效了 push_down的时候    如果有change 标记 可以很方便得求p[1] p[2] p[3] 如果mul存在  则也可以很方便求得子区间的各种值 同时由于mul 比add优先  要把add标记*=value  相当于本来每个加5  乘以

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 4902 线段树(区间更新)

Nice boat Time Limit: 30000/15000 MS (Java/Others)    Memory Limit: 131072/131072 K (Java/Others)Total Submission(s): 353    Accepted Submission(s): 169 Problem Description There is an old country and the king fell in love with a devil. The devil alw

hdu 4893 线段树 --- 也是两个变 类似双标记

http://acm.hdu.edu.cn/showproblem.php?pid=4893 开始的时候,我按双标记,WA了一下午,搞不定,我是用的两个标记add--表示当前结点中有值发生变化,flag,斐波那契的懒惰标记,但是估计是我自己处理的有问题,一直不对 参考了别人的代码,写法还是很不错的,Add变量维护的是,完全变成Fibonacci的时候的和,---回头我再重新写一遍 #include <cstdio> #include <cstring> #include <a

HDU 4902 线段树||暴力

给定一个序列,两种操作 1:把一段变成x. 2:把一段每个数字,如果他大于x,就变成他和x的gcd,求变换完后,最后的序列. 线段树解法:用lazy标记下即可,优化方法还是很巧妙的, Accepted 4902 515MS 3308K 1941 B C++ #include "stdio.h" #include "string.h" struct node { int l,r,x;// 在叶子节点代表值,树节点代表成端更新的lazy操作. }data[400010]

HDU 4302 线段树单点更新,维护区间最大最小值

http://acm.hdu.edu.cn/showproblem.php?pid=4302 Problem Description Holedox is a small animal which can be considered as one point. It lives in a straight pipe whose length is L. Holedox can only move along the pipe. Cakes may appear anywhere in the p

HDU 3397 线段树 双懒惰标记

这个是去年遗留历史问题,之前思路混乱,搞了好多发都是WA,就没做了 自从上次做了大白书上那个双重懒惰标记的题目,做这个就思路很清晰了 跟上次大白上那个差不多,这个也是有一个sets标记,代表这个区间全部置为0或者1,没有置位的时候为-1 还有个rev标记,代表翻转操作,0代表当前不翻,1代表当前翻 要注意一下优先级,发现有不同的弄法,我是这个弄得,有set操作的时候,set标记设值,并把当前节点的rev标记设为0,因为不管要不要rev,当前set操作肯定直接覆盖了 rev操作不改变set操作,在