计蒜客信息学赛前CSP-S2模拟赛2 T4 平均数
问题描述
有一个长度为 n* 的序列,求所有长度大于等于 m 的子区间的平均数
的最大值。
还有 q 次单点加操作,每次操作后输出长度大于等于 m 的子区间的平均数
的最大值。
输入格式
第一行三个正整数 \(n,M,q\),其中 \(M\geq\) 所有询问的 \(m\) 值。
第二行 \(n\) 个整数表示该序列。
下面 \(q\) 行每行三个正整数 \(x,c,m\),表示将位置 \(x\) 加 \(c\) 之后,长度大于等于 m 的子区间的平均数
的最大值。
输出格式
输出 \(q\) 行,第 \(i\) 行输出两个正整数 \(X,Y\),表示平均数的最大值为 \(X/Y\)。(\(X/Y\) 是最简分数)
数据范围
\(1\le n\le 125000,m\le \min(50,n),q\le 20000,a_i,c_i\le 10^9\)。
解析
对于一个询问,最后选择的区间长度不会超过 \(2m\) 。因为超过 \(2m\) 的区间我们一定可以把它划分为两个区间,满足这两个区间有一个的平均数大于原区间。
因此,我们不妨维护 \(2M\) 个线段树,第 \(k\) 个线段树中维护序列第 \(i\) 位开始的长度为 \(k\) 的区间和。单点加操作就是线段树区间修改。对于每一个询问,我们只需要查询第 \(m\) 到第 \(2m-1\) 棵线段树的最小值,然后更新答案即可。
实际上我们不需要同时维护 \(2M\) 个线段树。我们可以把询问离线下来,然后枚举长度并依次维护第 \(1\) 到第 \(2M\) 棵线段树。每棵线段树都更新一下所有能够更新的询问。
代码
#include <iostream>
#include <cstdio>
#include <cstring>
#define N 125002
using namespace std;
const double eps=1e-8;
struct query{
int x,c,m;
}q[N];
struct SegmentTree{
long long dat,add;
}t[N*4];
int n,m,p,i,l,a[N],ansy[N];
long long sum[N],ansx[N];
int read()
{
char c=getchar();
int w=0;
while(c<'0'||c>'9') c=getchar();
while(c<='9'&&c>='0'){
w=w*10+c-'0';
c=getchar();
}
return w;
}
void spread(int p)
{
if(t[p].add){
t[p*2].dat+=t[p].add;t[p*2+1].dat+=t[p].add;
t[p*2].add+=t[p].add;t[p*2+1].add+=t[p].add;
t[p].add=0;
}
}
void build(int p,int l,int r,int x)
{
t[p].dat=t[p].add=0;
if(l==r){
t[p].dat=sum[l+x-1]-sum[l-1];
return;
}
int mid=(l+r)/2;
build(p*2,l,mid,x);build(p*2+1,mid+1,r,x);
t[p].dat=max(t[p*2].dat,t[p*2+1].dat);
}
void change(int p,int l,int r,int ql,int qr,int x)
{
if(ql>qr) return;
if(ql<=l&&r<=qr){
t[p].dat+=x;t[p].add+=x;
return;
}
int mid=(l+r)/2;
spread(p);
if(ql<=mid) change(p*2,l,mid,ql,qr,x);
if(qr>mid) change(p*2+1,mid+1,r,ql,qr,x);
t[p].dat=max(t[p*2].dat,t[p*2+1].dat);
}
int gcd(int a,int b)
{
if(!b) return a;
return gcd(b,a%b);
}
signed main()
{
freopen("average.in","r",stdin);
freopen("average.out","w",stdout);
n=read();m=read();p=read();
for(i=1;i<=n;i++) a[i]=read();
for(i=1;i<=p;i++) q[i].x=read(),q[i].c=read(),q[i].m=read();
for(i=1;i<=n;i++) sum[i]=sum[i-1]+a[i];
for(l=1;l<=min(2*m-1,n);l++){
build(1,1,n-l+1,l);
for(i=1;i<=p;i++){
int L=max(1,q[i].x-l+1),R=min(n-l+1,q[i].x);
change(1,1,n-l+1,L,R,q[i].c);
if(l>=q[i].m&&l<2*q[i].m){
if(ansy[i]==0||(long double)t[1].dat/l-(long double)ansx[i]/ansy[i]>eps) ansx[i]=t[1].dat,ansy[i]=l;
}
}
}
for(i=1;i<=p;i++){
int d=gcd(ansx[i]%ansy[i],ansy[i]);
printf("%lld %d\n",ansx[i]/d,ansy[i]/d);
}
return 0;
}