大佬 场切这道题,而我还只能看题解
发现操作等价于,如果 a p + 1 − a p < l p a_{p+1}-a_p
发现操作后所有数的和不变,并且 a p + 1 a_{p+1} ap+1和 a p a_p ap的差恰好为 l p l_p lp
然后就是神来之笔了:考虑对于 > i >i >i的位置都减去 l i l_i li,这样操作就变成了 a p = a p + 1 = a p + a p + 1 2 a_p=a_{p+1}=\frac{a_p+a_{p+1}}{2} ap=ap+1=2ap+ap+1。为什么这样是对的?因为这样当操作 p p p的时候就合上了,而当操作 > p >p >p的位置时两个数之间的差不会改变,而是整体偏移一个值。可以证明,这样转化 不会改变 a 1 a_1 a1的值
然后,我们并不关心操作的过程,而只关心最后操作的结果。类似的,我们并不关心是否存在这种策略,而只关心能否达成
remark \text{remark} remark 我们只关心最终 a 1 a_1 a1的值,其他多余的性质不用去分析
结论:记 x x x为前缀平均值的最小值,那么最终 a 1 a_1 a1值就等于 x x x
证明:注意到每次操作只会让前缀平均值变小。考虑最小值的位置,如果可以分成若干个值相同的段,那么这些段的值一定是递增的,可以很容易的导出矛盾。
那么,我们将每个 a i a_i ai减去 x x x,问题就转化为了求任意前缀 ≥ 0 \ge 0 ≥0的序列数目
前缀和优化之,单次 D P DP DP复杂度 O ( n 3 ) O(n^3) O(n3)
发现有用的 x x x不会超过 max ( r i − l i ) \max(r_i-l_i) max(ri−li)个,因此剪枝即可。
复杂度 O ( n 2 d 2 ) O(n^2d^2) O(n2d2)。
remark \text{remark} remark 感觉考场上做的比较糟糕的原因还是在于 没有认真去分析题目想让你干啥 。。。
#include
#define ll long long
#define pb push_back
#define fi first
#define se second
#define inf 0x3f3f3f3f3f3f3f3f
using namespace std;
const int N=205;
const int mod=1e9+7;
int n,m,b[N],c[N],l[N],r[N],sl[N],sr[N];
ll res[200005],mul;
ll now[N*N],nxt[N*N];
ll solve(int x){
for(int i=1;i<=n;i++)sl[i]=sl[i-1]+l[i]-x,sr[i]=sr[i-1]+r[i]-x;
for(int i=1;i<=n;i++){
if(sr[i]<0)return 0;
}int ok=1;
for(int i=1;i<=n;i++){
if(sl[i]<0)ok=0;
}if(ok)return mul;
now[0]=1;
for(int i=1;i<=n;i++){
l[i]-=x,r[i]-=x;
for(int j=0;j<=sr[i]-sl[i];j++){
int L=max({0,sl[i-1],j+sl[i]-r[i]}),R=min(j+sl[i]-l[i],sr[i-1]);
if(L>R||j+sl[i]<0)nxt[j]=0;
else if(L==sl[i-1])nxt[j]=now[R-sl[i-1]];
else nxt[j]=(now[R-sl[i-1]]-now[L-sl[i-1]-1])%mod;
}for(int j=0;j<=sr[i]-sl[i];j++){
now[j]=nxt[j];if(j)now[j]=(now[j]+now[j-1])%mod;
}l[i]+=x,r[i]+=x;
}
return (now[sr[n]-sl[n]]+mod)%mod;
}
int main(){
ios::sync_with_stdio(false);
cin.tie(0),cout.tie(0);
cin>>n;for(int i=1;i<=n;i++)cin>>r[i],l[i]=0;
for(int i=1;i<n;i++){
cin>>b[i];
for(int j=i+1;j<=n;j++)l[j]-=b[i],r[j]-=b[i];
}mul=1;for(int i=1;i<=n;i++)mul=mul*(r[i]-l[i]+1)%mod;
memset(res,-1,sizeof res);
cin>>m;
for(int i=1;i<=m;i++){
int x;cin>>x;
if(~res[x+100000]){
cout<<res[x+100000]<<"\n";
}
else{
cout<<(res[x+100000]=solve(x))<<"\n";
}
}
}