【BZOJ3878】【Ahoi2014】奇怪的计算器 维护区间性质。线段树

广告:

#include <stdio.h>
int main()
{
    puts("转载请注明出处[vmurder]谢谢");
    puts("网址:blog.csdn.net/vmurder/article/details/44037685");
}

题解:

先排序然后插入线段树

用线段树每次对全区间进行操作。

然后维护哪些段区间溢出了,对这段区间进行赋值。

溢出处理:

一个区间的左端点大于最大值,或者右端点小于最小值

那么这个区间就该被覆盖。

覆盖,加特技:

乘0+x就是覆盖成x。

剪枝?:

一个区间右端点≤最大值,或者左端点≥最小值,那么return

数据一组:

3 3 3044
- 9999
@ 3
- 1367
2
850
127

代码:

#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>
#define N 101000
#define ls (note<<1)
#define rs (note<<1|1)
using namespace std;
struct LSH
{
    long long x;
    int p;
    bool operator < (const LSH &a)const
    {return x<a.x;}
    void read(int n){scanf("%lld",&x),p=n;}
}lsh[N];
struct OPT
{
    int f;
    long long a;
    void read(int p){scanf("%lld",&a),f=p;}
}opt[N];
int n,m;
long long L,R,ans[N];
struct KSD
{
    int l,r,s;
    long long A,B,C;
    long long L,R,lx,rx;
}s[N<<2];
inline void pushdown(int note) // 乘法C不影响其它
{
    if(s[note].C!=1)
    {
        s[note].L*=s[note].C;
        s[note].R*=s[note].C;

        if(s[note].l<s[note].r)
        {
            s[ls].A*=s[note].C;
            s[ls].B*=s[note].C;
            s[ls].C*=s[note].C;
            s[rs].A*=s[note].C;
            s[rs].B*=s[note].C;
            s[rs].C*=s[note].C;
        }

        s[note].C=1;
    }
    if(s[note].A)
    {
        s[note].L+=s[note].A;
        s[note].R+=s[note].A;

        if(s[note].l<s[note].r)
        {
            s[ls].A+=s[note].A;
            s[rs].A+=s[note].A;
        }

        s[note].A=0;
    }
    if(s[note].B)
    {
        s[note].L+=s[note].B*s[note].lx;
        s[note].R+=s[note].B*s[note].rx;

        if(s[note].l<s[note].r)
        {
            s[ls].B+=s[note].B;
            s[rs].B+=s[note].B;
        }

        s[note].B=0;
    }
    return ;
}
inline void pushup(int note)
{
    s[note].L=s[ls].L;
    s[note].R=s[rs].R;
    return ;
}
void build(int note,int l,int r)
{
    s[note].l=l,s[note].r=r;
    s[note].C=1;
    if(l==r)
    {
        s[note].L=s[note].R=s[note].lx=s[note].rx=lsh[l].x;
        s[note].s=lsh[l].p;
        return ;
    }
    int mid=l+r>>1;
    build(ls,l,mid),build(rs,mid+1,r);
    s[note].lx=s[ls].lx,s[note].rx=s[rs].rx;
    pushup(note);
    return ;
}
void add(int note,long long w,int f)
{
    if(f==0)s[note].A+=w;
    else s[note].B+=w;
    pushdown(note);
}
void mul(int note,long long w)
{
    s[note].A*=w,s[note].B*=w,s[note].C*=w;
    pushdown(note);
}
// void cover(x) -> mul(0),add(x,0)
void keep(int note)
{
    pushdown(note);
    if(s[note].L>R)
    {
        s[note].A=R;
        s[note].B=0;
        s[note].C=0;
        pushdown(note);
        return ;
    }
    if(s[note].R<L)
    {
        s[note].A=L;
        s[note].B=0;
        s[note].C=0;
        pushdown(note);
        return ;
    }
    if(s[note].R<=R&&s[note].L>=L)return ;
    int mid=s[note].l+s[note].r>>1;
    keep(ls),keep(rs),pushup(note);
}
void dfs(int note)
{
    pushdown(note);
    if(s[note].l==s[note].r)
    {
        ans[s[note].s]=s[note].L;
        return ;
    }
    dfs(ls),dfs(rs);
}
char ss[5];
int main()
{
    freopen("test.in","r",stdin);

    int i;
    scanf("%d%lld%lld",&m,&L,&R);
    for(i=1;i<=m;i++)
    {
        scanf("%s",ss);
        if(ss[0]==‘+‘)opt[i].read(1);
        else if(ss[0]==‘-‘)opt[i].read(2);
        else if(ss[0]==‘*‘)opt[i].read(3);
        else if(ss[0]==‘@‘)opt[i].read(4);
    }
    scanf("%d",&n);
    for(i=1;i<=n;i++)lsh[i].read(i);
    sort(lsh+1,lsh+n+1);
    build(1,1,n);
    for(i=1;i<=m;i++)
    {
        if(opt[i].f==1)add(1,opt[i].a,0);
        else if(opt[i].f==2)add(1,-opt[i].a,0);
        else if(opt[i].f==3)mul(1,opt[i].a);
        else if(opt[i].f==4)add(1,opt[i].a,1);
        keep(1);
    }
    dfs(1);
    for(i=1;i<=n;i++)printf("%lld\n",ans[i]);
    return 0;
}
时间: 2024-08-12 22:01:52

【BZOJ3878】【Ahoi2014】奇怪的计算器 维护区间性质。线段树的相关文章

bzoj1789 AHOI 维护数列(线段树)

首先想到线段树,然后刚开始写忽然想到树状数组求和岂不是更快,而且编程复杂度又小,于是把之前写的删掉,写树状数组,写完模版之后忽然发现这题竟然是区间修改! 于是又删掉重写,忽然发现不会处理又加又乘的,果断看题解-- 经过几乎两个小时的调试,终于1A. 需要注意的是,一定要让线段树的每一个区间保存的值时刻为正确的,这样才能在调用时直接返回.比如说这道题的change和query操作的最后一句话: sum:=f(g[k<<1]+g[k<<1+1]) 而不是 sum:=f(t[k<&

hdu 5700区间交(线段树)

区间交 Time Limit: 8000/4000 MS (Java/Others)    Memory Limit: 65536/65536 K (Java/Others)Total Submission(s): 849    Accepted Submission(s): 377 Problem Description 小A有一个含有n个非负整数的数列与m个区间.每个区间可以表示为li,ri. 它想选择其中k个区间, 使得这些区间的交的那些位置所对应的数的和最大. 例如样例中,选择[2,5]

CodeForces 52C Circular RMQ(区间循环线段树,区间更新,区间求和)

转载请注明出处:http://blog.csdn.net/u012860063 题目链接:http://codeforces.com/problemset/problem/52/C You are given circular array a0,?a1,?...,?an?-?1. There are two types of operations with it: inc(lf,?rg,?v) - this operation increases each element on the segm

区间覆盖(线段树)

区间覆盖(线段树) X轴上方有若干条平行于X轴的线段,求这些线段能够覆盖X轴的总长度? 输入格式 第一行一个数n(n<=100000),表示线段个数: 接下来n行,每行两个整数a[i],b[i](-10^8<=a[i],b[i]<=10^8),代表一个线段的两个端点输出覆盖X轴的长度 输入样例 2 10 12 2 4 输出样例 4 1 /* 2 样例: 3 有两段线 4 1 2 5 2 3 6 */ 7 8 #include <bits/stdc++.h> 9 using n

poj3468A Simple Problem with Integers区间和线段树

同上... #include <cstdio> #include <cstring> #include <algorithm> #include <climits> #include <string> #include <iostream> #include <map> #include <cstdlib> #include <list> #include <set> #include

【区间合并线段树】BZOJ1593-安排住宿

[题目大意] 查询最左端的连续长度区间:或批量修改一些区间.[思路] 区间合并线段树……复习一下.POJ上有一样的题目,我居然还借用了别人的权限号去做BZOJ,简直愚昧到没朋友[笑cry] 处理方法以前的博文里有,这里有不赘述了. 1 #include<iostream> 2 #include<cstdio> 3 #include<cstring> 4 #include<algorithm> 5 #include<cstdlib> 6 using

POJ 2528 Mayor&#39;s posters 区间离散化线段树

点击打开链接 Mayor's posters Time Limit: 1000MS   Memory Limit: 65536K Total Submissions: 45894   Accepted: 13290 Description The citizens of Bytetown, AB, could not stand that the candidates in the mayoral election campaign have been placing their elector

BZOJ 3878 Ahoi2014 奇怪的计算器 线段树

题目大意:给定n个操作,每个操作有四种形式,操作之后若<L就变成L,>R就变成R,现在给定q个输入,求他们的输出 n,q<=10W 将这q个数建立线段树,四个操作都可以在线段树上完成 但是溢出怎么办呢? 容易发现若x<=y,那么操作过后x一定<=y 因为四个操作都是线性的,溢出也不会反转两个数的大小关系 那么我们可以预先将q个数排序 那么溢出的数一定是连续的两段 区间修改就行了 至于怎么找两端溢出的区间-- 每个节点维护一个区间最大值和最小值就好了 此外数据还是比较良心的 不

bzoj 1798: [Ahoi2009]Seq 维护序列seq 线段树 区间乘法区间加法 区间求和

1798: [Ahoi2009]Seq 维护序列seq Time Limit: 1 Sec  Memory Limit: 256 MB 题目连接 http://www.lydsy.com/JudgeOnline/problem.php?id=1798 Description 老师交给小可可一个维护数列的任务,现在小可可希望你来帮他完成. 有长为N的数列,不妨设为a1,a2,…,aN .有如下三种操作形式: (1)把数列中的一段数全部乘一个值; (2)把数列中的一段数全部加一个值; (3)询问数列