P4243 [JSOI2009]等差数列
作者:互联网
好题。
我们的两种操作均是等差数列,那就来挖掘一下等差数列的性质。
那还用说,当然是相邻两项差相等啦。
发现其实就是求该序列的一个 差分序列 ,那么我们就搞出这个序列,这里令 \(s_{i}=a_{i+1}-a_{i}\) 。回头看操作一:
- 在原序列上一个区间 \([l, r]\) 加上一个等差数列。
求差分后,它就变成了:
-
在差分数列 l-1 位置处加上首项 a ;
-
对区间中每一项都加上公差 d ;
-
在差分数列 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,\ n-1]\) 去直接建树的,建议特判操作一中 l, r 的越界情况
-
计算答案时仍需合并,所以多开亿些结点备用。
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;
}
大常数选手喜提最优解。
标签:tmp,lc,int,JSOI2009,ulr,lr,rc,P4243,等差数列 来源: https://www.cnblogs.com/sizeof127/p/14614492.html