BZOJ1558 等差数列
题目链接:戳我
实话实话,看了几篇题解真的没看懂,我觉得讲的都有问题。这里对于线段树维护的s写了一点我自己的理解。
看到等差数列,我们考虑对数列做差,这样如果是等差数列,那么值应该相等。(比较容易维护,修改操作就变成了两个单点修改+一个区间修改,如果还是不理解的话可以参考一下代码)
查询比较麻烦,有这样的情况需要考虑:
举个例子——
1 3 5 6 9 12
它的差分数列为 2 2 1 3 3
最佳选择肯定是(1,3,5)(6,9,12),ans=2
但是我们纯看差分数列找相同的,ans=3
为什么会多算呢?原因就在于差分数列为1的这个位置其实是两个等差数列拼合点,不需要考虑在内。
所以我们设\(s[0],s[1],s[2],s[3]\)分别表示当前区间中,不考虑两端、不考虑左端点、不考虑右端点、两端都考虑,形成等差数列的个数。
两个区间合并的时候,左区间的右端点和右区间的左端点是至少要考虑一个的。因为如果都不考虑的娿,终究就遗漏了一个点了。两个都考虑时,如果中间差值相同,那么两个等差数列可以拼做一个,ans要-1.
注意查询的时候区间写(l,r-1)qwqwqwq
备注一下l,r记录的是左右端点的数值。
代码如下:
#include<iostream>
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<cmath>
#include<algorithm>
#include<queue>
using namespace std;
#define MAXN 120000
int v[MAXN],n;
struct Data{int s[4],l,r;};
struct Node
{
    int l,r,v;
    Data x;
}t[MAXN<<2];
Data operator + (Data x,Data y)
{
    Data cur;
    cur.l=x.l,cur.r=y.r;
    cur.s[0]=min(x.s[2]+y.s[0],x.s[0]+y.s[1]);
    cur.s[0]=min(cur.s[0],x.s[2]+y.s[1]-(y.l==x.r));
    cur.s[1]=min(x.s[1]+y.s[1],x.s[3]+y.s[0]);
    cur.s[1]=min(cur.s[1],x.s[3]+y.s[1]-(y.l==x.r));
    cur.s[2]=min(x.s[2]+y.s[2],x.s[0]+y.s[3]);
    cur.s[2]=min(cur.s[2],x.s[2]+y.s[3]-(y.l==x.r));
    cur.s[3]=min(x.s[3]+y.s[2],x.s[1]+y.s[3]);
    cur.s[3]=min(cur.s[3],x.s[3]+y.s[3]-(y.l==x.r));
    return cur;
}
inline int ls(int x){return x<<1;}
inline int rs(int x){return x<<1|1;}
inline void solve(int now,int k)
{
    t[now].v+=k;
    t[now].x.l+=k;
    t[now].x.r+=k;
}
inline void pushdown(int now)
{
    if(t[now].v)
    {
        solve(ls(now),t[now].v);
        solve(rs(now),t[now].v);
        t[now].v=0;
    }
}
inline void Build(int now,int l,int r)
{
    t[now].l=l,t[now].r=r;
    if(l==r)
    {
        t[now].x.s[0]=0;
        t[now].x.s[1]=t[now].x.s[2]=t[now].x.s[3]=1;
        t[now].x.l=t[now].x.r=v[l];
        return;
    }
    int mid=(l+r)>>1;
    Build(ls(now),l,mid);
    Build(rs(now),mid+1,r);
    t[now].x=t[ls(now)].x+t[rs(now)].x;
}
inline Data Query(int now,int ll,int rr)
{
    int l=t[now].l,r=t[now].r;
    if(l==ll&&r==rr) return t[now].x;
    pushdown(now);
    int mid=(l+r)>>1;
    if(rr<=mid) return Query(ls(now),ll,rr);
    if(mid<ll) return Query(rs(now),ll,rr);
    return Query(ls(now),ll,mid)+Query(rs(now),mid+1,rr);
}
inline void Modify(int now,int ll,int rr,int k)
{
    int L=t[now].l,R=t[now].r;
    if(L==ll&&R==rr)
    {
        t[now].v+=k;
        t[now].x.l+=k,t[now].x.r+=k;
        return;
    }
    pushdown(now);
    int mid=(L+R)>>1;
    if(rr<=mid) Modify(ls(now),ll,rr,k);
    else if(mid<ll) Modify(rs(now),ll,rr,k);
    else
    {
        Modify(ls(now),ll,mid,k);
        Modify(rs(now),mid+1,rr,k);
    }
    t[now].x=t[ls(now)].x+t[rs(now)].x;
}
int main()
{
    #ifndef ONLINE_JUDGE
    freopen("ce.in","r",stdin);
    freopen("std.out","w",stdout);
    #endif
    scanf("%d",&n);
    for(int i=1;i<=n;i++) scanf("%d",&v[i]);
    for(int i=1;i<n;i++) v[i]=v[i+1]-v[i];
    Build(1,1,n-1);
    int Q;
    scanf("%d",&Q);
    for(int i=1;i<=Q;i++)
    {
        char op[10];
        int s,t,a,b;
        scanf("%s",op);
        if(op[0]=='B')
        {
            scanf("%d%d",&s,&t);
            if(s==t) printf("1\n");
            else printf("%d\n",Query(1,s,t-1).s[3]);
        }
        else if(op[0]=='A')
        {
            scanf("%d%d%d%d",&s,&t,&a,&b);
            if(s!=1) Modify(1,s-1,s-1,a);
            if(s!=t) Modify(1,s,t-1,b);
            if(t!=n) Modify(1,t,t,-(t-s)*b-a);
        }
    }
    return 0;
}
												
											BZOJ1558 等差数列的更多相关文章
- OI题目类型总结整理
		
## 本蒟蒻的小整理qwq--持续更新(咕咕咕) 数据结构 数据结构 知识点梳理 数据结构--线段树 推荐yyb dalao的总结--戳我 以后维护线段树还是把l,r写到struct里面吧,也别写le ...
 - 【BZOJ1558】等差数列(线段树)
		
[BZOJ1558]等差数列(线段树) 题面 BZOJ 题解 可以说这道题已经非常毒瘤了 怎么考虑询问操作? 如果直接将一段数分解为等差数列? 太麻烦了.... 考虑相邻的数做差, 这样等差数列变为了 ...
 - BZOJ1558 [JSOI2009]等差数列  【线段树】
		
题目链接 BZOJ1558 题解 等差数列,当然是差分一下 差分值相同的连续位置形成等差数列,我们所选的两个等差数列之间可以有一个位置舍弃 例如: \(1 \; 2 \; 3 \; 6 \; 8 \; ...
 - [bzoj1558][JSOI2009]等差数列
		
题目:给定n个数,m个操作,每次给一段区间加一个等差数列或者询问一段区间至少要用多少个等差数列来表示.$n,m\leqslant 10^{5}$ 题解:老套路,维护差分数组,修改操作变成了两个单点加和 ...
 - 洛谷P4243/bzoj1558 [JSOI2009]等差数列(线段树维护差分+爆炸恶心的合并)
		
题面 首先感谢这篇题解,是思路来源 看到等差数列,就会想到差分,又有区间加,很容易想到线段树维护差分.再注意点细节,\(A\)操作完美解决 然后就是爆炸恶心的\(B\)操作,之前看一堆题解的解释都不怎 ...
 - 等差数列(bzoj 3357)
		
Description 约翰发现奶牛经常排成等差数列的号码.他看到五头牛排成这样的序号:"1,4,3,5,7" 很容易看出"1,3,5,7"是等差数列. ...
 - 3357: [Usaco2004]等差数列
		
3357: [Usaco2004]等差数列 Time Limit: 10 Sec Memory Limit: 128 MBSubmit: 321 Solved: 153[Submit][Statu ...
 - Find Missing Term in Arithmetic Progression 等差数列缺失项
		
查找等差数列中的缺失项. e.g.Input: arr[] = {2, 4, 8, 10, 12, 14} Output: 6 Input: arr[] = {1, 6, 11, 16, 21, 31 ...
 - n个整数中,找出尽可能多的数使他们组成一个等差数列,求最长等差数列的长度
		
例子: 3,8,4,5,6,2 返回值应该为 :5 这是昨天做的一道优酷土豆的编程题,和leetcode中的128/ Longest Consecutive Sequence 有点 ...
 
随机推荐
- 关于微信小程序的一些看法和理解
			
最近做了几个小时的调研,微信小程序 微信小程序是可以理解成在微信中的APP,他的目标是APP的替代者,由于目前的APP主要区分安卓和IOS,或者其他平台, 那么微信小程序的平台在微信,在任何一个手机系 ...
 - ES6系列_16之模块化操作
			
ES6的模块化操作主要包括两个方面. (1)export :负责进行模块化,也是模块的输出. (2)import : 负责把模块引,也是模块的引入操作. export的用法: export可以让我们把 ...
 - 方便好使的java.util.Properties类
			
今天偶然碰到这个类,发现jdk中这些平时不大用到的类还挺好玩儿的,用起来也特别实在方便,随便写点记录下. java.util.Properties是对properties这类配置文件的映射.支持key ...
 - VisualStudio2012轻松把JSON数据转换到POCO的代码(转)
			
VisualStudio2012轻松把JSON数据转换到POCO的代码 在Visual Studio 2012中轻松把JSON数据转换到POCO的代码,首先你需要安装Web Essentials 20 ...
 - JDK静态代理示例代码
			
JDK静态代理示例代码 业务接口 接口的实现类 代理类,实现接口,并扩展实现类的功能 1.业务接口 /** * 业务接口 * @author pc * */ public interface User ...
 - Django+python实现网页数据的excel导出
			
一直都想做一个网页的excel导出功能,最近抽时间研究了下,使用urllib2与BeautifulSoup及xlwt模块实现 urllib2这个模块之前有用过,关于BeautifulSoup模块,可参 ...
 - OLI 课程 & Java入学考试的五道题
			
Unit 1:: Programming with Java ✔️ 机械.自动.不需要智慧地执行原始的内置指令. 字节码相同,JVM不同(体现平台) ✖️ In modern computers i ...
 - 5-青蛙的约会(ex_gcd)
			
青蛙的约会 Time Limit: 1000MS Memory Limit: 10000K Total Submissions:122411 Accepted: 25980 Descripti ...
 - std::time(0)找不到
			
http://zh.cppreference.com/w/cpp/chrono/c/time #include <ctime> isnan找不到 http://en.cppreferenc ...
 - openpose
			
编译libpthread.so pthread2 ihash