码迷,mamicode.com
首页 > Web开发 > 详细

P4243 [JSOI2009]等差数列

时间:2021-04-05 12:47:16      阅读:0      评论:0      收藏:0      [点我收藏+]

标签:down   需要   query   printf   端点   建议   维护   tip   i+1   

好题。

我们的两种操作均是等差数列,那就来挖掘一下等差数列的性质。

那还用说,当然是相邻两项差相等啦。

发现其实就是求该序列的一个 差分序列 ,那么我们就搞出这个序列,这里令 \(s_{i}=a_{i+1}-a_{i}\) 。回头看操作一:

  • 在原序列上一个区间 \([l, r]\) 加上一个等差数列。

求差分后,它就变成了:

  1. 在差分数列 l-1 位置处加上首项 a ;

  2. 对区间中每一项都加上公差 d ;

  3. 在差分数列 r+1 位置处减去 (r-l+1)*d+a 。

再看操作二(在原数列上):

  • 求最少的划分段,使得每段均为一个等差数列。

其等价于(在差分数列上):

  • 求最少的划分段,使得每一段内的数都相等。

然鹅问题并没有变得容易。

几经思索后,我们发现只需要维护四个值即可:

  • 分割区间 \([l,\ r]\) 的最少划分段;

  • 分割区间 \([l,\ r)\) 的最少划分段;

  • 分割区间 \((l,\ r]\) 的最少划分段;

  • 分割区间 \((l,\ r)\) 的最少划分段。

为了方便,顺便维护区间左右端点的值。

那么容易推出以下 pushup 函数:

void pushup(int k, int lc, int rc){
		st(k)=st(lc), ed(k)=ed(rc);int tmp=ed(lc)==st(rc);
        l(k)=man(l(lc)+lr(rc)-tmp, ulr(lc)+lr(rc), l(rc)+l(lc));
        r(k)=man(lr(lc)+r(rc)-tmp, lr(lc)+ulr(rc), r(rc)+r(lc));
        lr(k)=man(lr(lc)+lr(rc)-tmp, r(lc)+lr(rc), l(rc)+lr(lc));
		ulr(k)=man(l(lc)+r(rc)-tmp, ulr(lc)+r(rc), ulr(rc)+l(lc));
	}

其它部分不难实现。

Tip:

  1. 如果你是以区间 \([1,\ n-1]\) 去直接建树的,建议特判操作一中 l, r 的越界情况

  2. 计算答案时仍需合并,所以多开亿些结点备用。

Code

#include <stdio.h>
#include <algorithm>
#include <string.h>
#define ls k<<1
#define rs k<<1|1
#define N 100005
using namespace std;
typedef long long ll;
int n, m, a[N], INF;
struct Segment_Tree{
	int cnt;
	struct node{
		ll st, ed;ll tag;
		int uselr, unuselr, usel, user;
		#define st(k)data[k].st
		#define ed(k)data[k].ed
		#define l(k)data[k].usel
		#define r(k)data[k].user
		#define lr(k)data[k].uselr
		#define ulr(k)data[k].unuselr
		#define t(k)data[k].tag
	}data[N*4];
	int man(int a, int b, int c){
		return min(a, min(b, c));
	}
	void pushup(int k, int lc, int rc){
		st(k)=st(lc), ed(k)=ed(rc);int tmp=ed(lc)==st(rc);
        l(k)=man(l(lc)+lr(rc)-tmp, ulr(lc)+lr(rc), l(rc)+l(lc));
        r(k)=man(lr(lc)+r(rc)-tmp, lr(lc)+ulr(rc), r(rc)+r(lc));
        lr(k)=man(lr(lc)+lr(rc)-tmp, r(lc)+lr(rc), l(rc)+lr(lc));
		ulr(k)=man(l(lc)+r(rc)-tmp, ulr(lc)+r(rc), ulr(rc)+l(lc));
	}
	void upd(int k, ll tag){
		st(k)+=tag, ed(k)+=tag, t(k)+=tag;
	}
	void pushdown(int k){
		if(!t(k)) return ;
		upd(ls, t(k));upd(rs, t(k));
		t(k)=0;
	}
	void build(int k, int l, int r){
		INF=max(INF, k);
		if(l==r){
			st(k)=ed(k)=a[l&r];
			lr(k)=l(k)=r(k)=1;ulr(k)=t(k)=0;
			return ;
		}
		int mid=l+r>>1;
		build(ls, l, mid);build(rs, mid+1, r);
		pushup(k, ls, rs);
	}
	void add(int k, int l, int r, int x, int y, int v){
		if(x<=l&&r<=y) return upd(k, v);
		int mid=l+r>>1;pushdown(k);
		if(x<=mid) add(ls, l, mid, x, y, v);
		if(mid<y) add(rs, mid+1, r, x, y, v);
		pushup(k, ls, rs);
	}
	int query(int k, int l, int r, int x, int y){
		if(x<=l&&r<=y) return k;
		int mid=l+r>>1, tmp1=0, tmp2=0;pushdown(k);
		if(x<=mid) tmp1=query(ls, l, mid, x, y);
		if(mid<y) tmp2=query(rs, mid+1, r, x, y);
		if(tmp1&&tmp2) pushup(++cnt, tmp1, tmp2);
		if(tmp1&&tmp2) return cnt;
		else return tmp1?tmp1:tmp2;
	}
}seg;
char opt[4];
int main(){
	scanf("%d", &n);
	for(int i=1; i<=n; i++) scanf("%d", &a[i]);n--;
	for(int i=1; i<=n; i++) a[i]=a[i+1]-a[i];
	seg.build(1, 1, n);scanf("%d", &m);
	for(int i=1, l, r, a, b; i<=m; i++){
		scanf("%s%d%d", opt, &l, &r);
		if(opt[0]==‘A‘){
			scanf("%d%d", &a, &b);
			if(l!=1) seg.add(1, 1, n, l-1, l-1, a);
            if(r!=n+1) seg.add(1, 1, n, r, r, -(a+b*(r-l)));
            if(l!=r) seg.add(1 ,1 ,n, l, r-1, b);
		}
		else{
			if(l==r) printf("1\n");
			else{
				seg.cnt=INF;int pos=seg.query(1, 1, n, l, r-1);
				printf("%d\n", seg.data[pos].uselr);
			} 
		}
	}
	return 0;
}

大常数选手喜提最优解。

P4243 [JSOI2009]等差数列

标签:down   需要   query   printf   端点   建议   维护   tip   i+1   

原文地址:https://www.cnblogs.com/sizeof127/p/14614492.html

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!