这题应当有很多做法
拿到题目以后有很多想法,光是不同的线段树就有两三种,而且我觉得分块卡一卡也是能卡过去的。
最后写了最简单的方法,代码比起其它的解法都短很多。
考虑差分后的数组,原问题变可以转化成求最长的一段正数后紧接一段负数的长度。
若用折线将这串数字连接起来,那么每一段“合唱队形”数便可以表示成两个波谷之间的距离。
线段树维护所有的波谷,维护最大的两个波谷之间的距离。
修改直接在差分数组上单点修改,对点x修改的时候可能会产生/消失波谷的位置是x-1,x,x+1,对这三个点进行单点修改即可。
Notice:
1、差分数组可能会加爆int,需要开long long
2、波峰波谷在临界值和最左最右端的取值方法细节需要注意
#include
#include
#define mid ((l+r)>>1)
#define ls l,mid,2*t
#define rs mid+1,r,2*t+1
#define N 300050
using namespace std;
typedef long long LL;
inline int rd() { int t; scanf("%d",&t); return t; }
inline void ut(int &x,int y) { x = max(x,y); }
struct Tree{ int lb,rb,ans; }tr[4*N];
int tmp[N],n,bg[N];
LL a[N];
int dis(int x,int y) {
if (!x || !y) return 0;
int r = x - y;
if (x == y+1 && a[x] == 0) return 1;
return r+1;
if (a[y+1] > 0 && a[x] < 0) r++;
return r;
}
Tree merge(Tree p1,Tree p2) {
Tree tmp;
tmp.lb = p1.lb ? p1.lb : p2.lb;
tmp.rb = p2.rb ? p2.rb : p1.rb;
tmp.ans = max(p1.ans , p2.ans);
ut(tmp.ans , dis( p2.lb , p1.rb ));
return tmp;
}
void build(int l,int r,int t) {
if (l == r) {
tr[t].lb = tr[t].rb = bg[l] > 0 ? l : 0;
tr[t].ans = 1;
return ; }
build(ls); build(rs);
tr[t] = merge(tr[2*t] , tr[2*t+1]);
}
int x;
void update(int l,int r,int t) {
if (l > x || r < x) return ;
if (l == r) {
tr[t].lb = tr[t].rb = bg[l] > 0 ? l : 0;
tr[t].ans = 1;
return ; }
update(ls); update(rs);
tr[t] = merge(tr[2*t] , tr[2*t+1]);
}
void init() {
n=rd();
tmp[0] = -1;
for (int i=1;i<=n;i++) tmp[i] = rd();
for (int i=2;i<=n;i++) a[i] = tmp[i] - tmp[i-1];
for (int i=1;i<=n;i++) if ((a[i]<0 && a[i+1]>0) || (!a[i]) || (!a[i+1])) bg[i] = 1;
build(1,n,1);
}
inline void c(int t) {
if (t<1 || t>n) return ;
bg[x=t] = (a[t]<0 && a[t+1]>0) || (!a[t]) || (!a[t+1]);
update(1,n,1);
}
void solve() {
int m = rd();
while (m--) {
int ql=rd() , qr=rd() , d=rd();
a[ql] += 1LL * d; if (qr+1 <= n) a[qr+1] -= 1LL * d; a[1] = 0;
c(ql-1);
c(ql);
c(ql+1);
c(qr-1);
c(qr);
c(qr+1);
int ans = tr[1].ans;
if (tr[1].lb) ut(ans,tr[1].lb);
if (tr[1].rb) ut(ans,n-tr[1].rb+1);
if (!tr[1].lb && !tr[1].rb) ut(ans,n);
printf("%d\n",ans);
}
}
int main() {
#ifndef ONLINE_JUDGE
freopen("2.in","r",stdin);
freopen("2.out","w",stdout);
#endif
init();
solve();
return 0;
}
对拍暴力程序:
直接暴力处理一个数向左/向右单调下降序列的长度,两个叠加-1更新答案即可。n^2的暴力可以跑很大的数据。
#include
#include
#define N 100050
#define INF (1<<30)
using namespace std;
typedef long long LL;
inline void ut(int &x,int y) { x = max(x,y); }
LL a[N];
int l[N],r[N],n,m;
int main() {
freopen("2.in","r",stdin);
freopen("2_cmp.out","w",stdout);
scanf("%d",&n);
for (int i=1;i<=n;i++) scanf("%I64d",&a[i]);
a[0] = a[n+1] = -INF;
scanf("%d",&m);
while (m--) {
int ll,rr,d; scanf("%d%d%d",&ll,&rr,&d);
for (int i=ll;i<=rr;i++) a[i] += d;
for (int i=1;i<=n;i++) l[i] = (a[i]>a[i-1]) ? l[i-1]+1 : 1;
for (int i=n;i>=1;i--) r[i] = (a[i]>a[i+1]) ? r[i+1]+1 : 1;
int ans = -INF;
for (int i=1;i<=n;i++) ut(ans,l[i]+r[i]-1);
printf("%d\n",ans);
}
return 0;
}
造数据程序:
随机数组随机l,r,d,很简单的造数据程序。可以构造n=1和n=2两组天坑数据来卡你的程序。
#include
#include
#include
#include
using namespace std;
const int maxn = 10000;
const int top = 10000;
const int maxm = 10000;
int main() {
freopen("2.in","w",stdout);
srand(time(0));
int n = rand() % maxn + 1; printf("%d\n",n);
for (int i=1;i<=n;i++) printf("%d%c",rand() % top + 1,i==n?'\n':' ');
int m = rand() % maxm + 1; printf("%d\n",m);
for (int i=1;i<=m;i++) {
int l = rand() % n + 1;
int r = rand() % (n-l+1) + l;
int d = rand() % top + 1;
printf("%d %d %d\n",l,r,d);
}
return 0;
}