题目链接:codeforces.com/contest/203…
+1 +2 +1,就是中间的数变大,两边的数变小,对一系列数进行一个这样的操作:
......不动 不动 不动 操作 不动 操作 不动 操作 不动 操作 不动 不动 不动.........
就可以实现:将一些列数中,中间的数+2,首尾的数+1,+2的数用红色表示,+1的数用蓝色表示,以上操作后的结果:
......不动 不动 不动 操作 不动 操作 不动 操作 不动 操作 不动 不动 不动.........
这样利用+2和+1的差异,可以对一个区间两端点的数的差进行修改,这是最关键的需要意识到的地方,还有一个关键点如下,我们引入负操作:
对一列数中,除第i个数外,所有的数进行操作,如果关注这些数之间的相对大小,那么相当于对第i个数-2,对第i个数两边的数分别-1。负操作和正操作可以互相抵消,因为一次负操作是:
-1 -2 -1而正操作是 1 2 1,所以可以抵消。
之后使用常用方法思考:
对于n=1 ,已经balanced
对于n=3, a1 a2 a3,三个数,可以先针对两端点,达到 a1=a3,对a1进行操作,操作数为a3-a1(可正可负)。
然后再对a2进行操作,使a1=a2=a3。
类似的,对于a1.....an,n个数,
先达到端点相等
i from 1 to n/2,依次让a[i]=a[n-i+1]:
令d=a[n-i+1]-a[i]
区间从位置i开始,向左,直到1,每隔2个数进行d次操作。(d可正可负)
从位置n-i+2开始,向右,直到n,每隔两个数进行d次操作。
尤其注意:对a1修改后,a2也跟着修改了,a2的变动会影响a2和a[n-1]达到相等的过程。
再从小区间相等达到大区间相等
现在有了,a1=an a2=a[n-1] ...,再从中间往两边,每次修改靠中间的两个对称位置上的数,使这两个数和他们中间所夹的数相等。
#include<iostream>
#include<cstdio>
#include<climits>
#include<cstring>
using namespace std;
#define ll long long
#define endl "\n"
const int maxn=2*1e5+20;
ll D[2][maxn],ans[maxn];
ll a[maxn];
int n;
inline int nxt(int x) {return x==n? 1:x+1 ;}
inline int lst(int x) {return x==1? n:x-1 ;}
int main()
{
//freopen("D:\in.txt","r",stdin);
ios::sync_with_stdio(false);
cin.tie(nullptr);
int t;
cin>>t;
while(t--)
{
cin>>n;
for(int i=0;i<=n;i++) D[0][i]=D[1][i]=0;
for(int i=1;i<=n;i++) cin>>a[i];
//! a[i] must be updated
ll delta=0;
for(int i=1;i<=n/2;i++)
{
int l=i,r=n-i+1;
ll d=a[r]-(a[l]+delta);
delta=d;
D[i%2][1]+=d;
D[i%2][i+1]-=d;
D[(i%2)^1][n+2-i]+=d;
D[(i%2)^1][n+1]-=d;
}
for(int i=1;i<=n;i++)
{
D[0][i]+=D[0][i-1];
D[1][i]+=D[1][i-1];
ans[i]=D[i%2][i];
a[i]+=ans[i]*2;
a[lst(i)]+=ans[i];
a[nxt(i)]+=ans[i];
}
for(int i=0;i<=n;i++) D[0][i]=D[1][i]=0;
for(int i=n/2;i>=1;i--)
{
ll d=a[i]-a[i+1];
D[(i+1)%2][i+1]+=d;
D[(i+1)%2][n-i+1]-=d;
a[i]+=d;
}
for(int i=1;i<=n;i++)
{
D[0][i]+=D[0][i-1];
D[1][i]+=D[1][i-1];
ans[i]+=D[i%2][i];
}
ll minn=LLONG_MAX;
for(int i=1;i<=n;i++) minn=min(minn,ans[i]);
for(int i=1;i<=n;i++) ans[i]-=minn;
for(int i=1;i<=n;i++) cout<<ans[i]<<" ";
cout<<endl;
}
return 0;
}