Wannafly 挑战赛22 D 整数序列 线段树 区间更新,区间查询

题目链接:https://www.nowcoder.com/acm/contest/160/D

时间限制:C/C++ 2秒,其他语言4秒
空间限制:C/C++ 262144K,其他语言524288K
64bit IO Format: %lld

题目描述

给出一个长度为n的整数序列a1,a2,...,an,进行m次操作,操作分为两类。
操作1:给出l,r,v,将al,al+1,...,ar分别加上v;
操作2:给出l,r,询问

输入描述:

n个数,m次操作op=1, l,r,v 区间[l,r] 加vop=2, l,r 区间查询上式n,m,val[i] <= 2e5

输出描述:

对每个操作2,输出一行,表示答案,四舍五入保留一位小数保证答案的绝对值大于0.1,且答案的准确值的小数点后第二位不是4或5数据随机生成(n,m人工指定,其余整数在数据范围内均匀选取),并去除不满足条件的操作2
输入例子:
4
1 2 3 4
5
2 2 4
1 1 3 1
2 2 4
1 2 4 2
2 1 3
输出例子:
0.3
-1.4
-0.3

-->

示例1

输入

复制

4
1 2 3 4
5
2 2 4
1 1 3 1
2 2 4
1 2 4 2
2 1 3

输出

复制

0.3
-1.4
-0.3

很明显区间更新的题 用lazy维护某一段区间的 sin(val[i])和 cos(val[i])值根据下面的式子  可知道 要保证区间加和性,就要先要 维护sin(x) 区间加和的性质so 看代码 和 公式 应该差不多能理解的
// sin(x + v) = sinx*cosv+sinvcosx
// cos(x + v) = cosx*cosv-sinx*sinv

// sin(x1 + v) + sin(x2 + v) = cosv*(sinx1+sinx2) + sinv*(cosx1+cosx2)
// cos(x1 + v) + cos(x1 + v) = cosv*(cosx1+cosx2) - sinv*(sinx1+sinx2)
#include<bits/stdc++.h>
using namespace std;

const int N = 200000+10;
#define ls rt<<1
#define rs rt<<1|1
typedef long long ll;

ll val[N], lz[N<<3];
double ssin[N<<3],ccos[N<<3];

void up(int rt) {
    ssin[rt] = ssin[ls] + ssin[rs];
    ccos[rt] = ccos[ls] + ccos[rs];
}

void down(int rt) {
    if(lz[rt]) {
        ll v = lz[rt];
        lz[ls] += v;
        lz[rs] += v;

        double tsin=ssin[ls], tcos=ccos[ls];
        ssin[ls]=tsin*cos(v) + tcos*sin(v);
        ccos[ls]=tcos*cos(v) - tsin*sin(v);

        tsin=ssin[rs],tcos=ccos[rs];
        ssin[rs]=tsin*cos(v) + tcos*sin(v);
        ccos[rs]=tcos*cos(v) - tsin*sin(v);
        lz[rt]=0;
    }
}

void build(int rt,int l,int r) {
    if(l==r) {
        ssin[rt]=sin(val[l]);
        ccos[rt]=cos(val[l]);
        return ;
    }
    int m = (l+r)/2;
    build(ls,l,m);
    build(rs,m+1,r);
    up(rt);
}

void update(int rt,int l,int r,int L,int R,ll v) {
    if(L <= l && r <= R) {
        lz[rt] += v;
        double tsin=ssin[rt], tcos=ccos[rt];
        ssin[rt]=tsin*cos(v) + tcos*sin(v);
        ccos[rt]=tcos*cos(v) - tsin*sin(v);
        return ;
    }
    down(rt);
    int m = (l+r)/2;
    if(L <= m)
        update(ls,l,m,L,R,v);
    if(m < R)
        update(rs,m+1,r,L,R,v);
    up(rt);
}

double query(int rt,int l,int r,int L,int R) {
    if(L<=l && r<=R) {
        return ssin[rt];
    }
    down(rt);
    int m = (l+r)/2;
    double res = 0;
    if(L <= m)
        res += query(ls,l,m,L,R);
    if(m < R)
        res += query(rs,m+1,r,L,R);
    return res;
}

int n,m;
int main ()
{
    //freopen("in.txt","r",stdin);
    while (scanf("%d",&n)!=EOF) {
        memset(lz,0,sizeof(lz));
        for(int i=1;i<=n;i++) {
            scanf("%lld", &val[i]);
        }
        build(1,1,n);
        scanf("%d", &m);
        while (m--){
            int op,l,r;
            scanf("%d %d %d",&op,&l,&r);
            if(op==1) {
                ll v; scanf("%lld",&v);
                update(1,1,n,l,r,v);
            }else {
                printf("%.1f\n",query(1,1,n,l,r));
            }
        }
    }
    return 0;
}

原文地址:https://www.cnblogs.com/Draymonder/p/9532434.html

时间: 2024-10-03 14:32:54

Wannafly 挑战赛22 D 整数序列 线段树 区间更新,区间查询的相关文章

POJ 3468 A Simple Problem with Integers 线段树 区间更新 区间查询

题目链接: http://poj.org/problem?id=3468 题目描述: 一组数列, 可进行一段区间加上某一个数, 和区间查询 解题思路: 线段树, 之前的那道题是求总区间直接输出sum[1] 就可以了, 这次有了区间查询, 同理, 查询的时候Pushdown 代码: #include <iostream> #include <cstdio> #include <string> #include <vector> #include <map

CDOJ 1057 秋实大哥与花 线段树 区间更新+区间查询

链接: I - 秋实大哥与花 Time Limit:1000MS     Memory Limit:65535KB     64bit IO Format:%lld & %llu Submit Status Practice UESTC 1057 Appoint description:  System Crawler  (2016-04-19) Description 秋实大哥是一个儒雅之人,昼听笙歌夜醉眠,若非月下即花前. 所以秋实大哥精心照料了很多花朵.现在所有的花朵排成了一行,每朵花有一

POJ-3468(线段树+区间更新+区间查询)

A Simple Problem With Integers POJ-3468 这题是区间更新的模板题,也只是区间更新和区间查询和的简单使用. 代码中需要注意的点我都已经标注出来了,容易搞混的就是update函数里面还需要计算sum数组.因为这里查询的时候是直接用sum查询结点. //区间更新,区间查询 #include<iostream> #include<cstdio> #include<algorithm> #include<cstring> #inc

HDU 4902 Nice boat(线段树 区间更新)

Nice boat 大意:给你一个区间,每次可以进行两种操作,1:把区间中的数全都变成x  2:把区间中大于x的数变成gcd(a[i], x),最后输出序列. 思路:线段树成段更行,用num数组的叶子存储数据,节点当作lazy来使用. 1 #include <stdio.h> 2 const int maxn = 100005; 3 4 int num[maxn<<2]; 5 6 int gcd(int a, int b){ 7 return b?gcd(b, a%b):a; 8

hihoCoder #1078 : 线段树的区间修改(线段树区间更新板子题)

#1078 : 线段树的区间修改 时间限制:10000ms 单点时限:1000ms 内存限制:256MB 描述 对于小Ho表现出的对线段树的理解,小Hi表示挺满意的,但是满意就够了么?于是小Hi将问题改了改,又出给了小Ho: 假设货架上从左到右摆放了N种商品,并且依次标号为1到N,其中标号为i的商品的价格为Pi.小Hi的每次操作分为两种可能,第一种是修改价格——小Hi给出一段区间[L, R]和一个新的价格NewP,所有标号在这段区间中的商品的价格都变成NewP.第二种操作是询问——小Hi给出一段

hdu6070(分数规划/二分+线段树区间更新,区间最值)

题目链接: http://acm.hdu.edu.cn/showproblem.php?pid=6070 题意: 给出一个题目提交序列, 从中选出一个正确率最小的子串. 选中的子串中每个题目当且仅当最后一次提交是正确的. 思路: 分数规划 二分答案, 然后在 check 函数中查找是否存在某个区j间 [l, r] 使得 sum(l, r) / (r - l + 1) <= mid, 即 sum(l, r) + l * mid <= (r + 1) * mid. 可以用个线段树来维护 sum(l

HDU 4902 Nice boat 2014杭电多校训练赛第四场F题(线段树区间更新)

题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=4902 解题报告:输入一个序列,然后有q次操作,操作有两种,第一种是把区间 (l,r) 变成x,第二种是把区间 (l,r) 中大于x的数跟 x 做gcd操作. 线段树区间更新的题目,每个节点保存一个最大和最小值,当该节点的最大值和最小值相等的时候表示这个区间所有的数字都是相同的,可以直接对这个区间进行1或2操作, 进行1操作时,当还没有到达要操作的区间但已经出现了节点的最大值跟最小值相等的情况时,说明

POJ 2528 Mayor&#39;s posters (线段树区间更新+离散化)

题目链接:http://poj.org/problem?id=2528 给你n块木板,每块木板有起始和终点,按顺序放置,问最终能看到几块木板. 很明显的线段树区间更新问题,每次放置木板就更新区间里的值.由于l和r范围比较大,内存就不够了,所以就用离散化的技巧 比如将1 4化为1 2,范围缩小,但是不影响答案. 写了这题之后对区间更新的理解有点加深了,重点在覆盖的理解(更新左右两个孩子节点,然后值清空),还是要多做做题目. 1 #include <iostream> 2 #include <

Hdu 3966 Aragorn&#39;s Story (树链剖分 + 线段树区间更新)

题目链接: Hdu 3966 Aragorn's Story 题目描述: 给出一个树,每个节点都有一个权值,有三种操作: 1:( I, i, j, x ) 从i到j的路径上经过的节点全部都加上x: 2:( D, i, j, x ) 从i到j的路径上经过的节点全部都减去x: 3:(Q, x) 查询节点x的权值为多少? 解题思路: 可以用树链剖分对节点进行hash,然后用线段树维护(修改,查询),数据范围比较大,要对线段树进行区间更新 1 #include <cstdio> 2 #include