CF1416B Make Them Equal 题解
分析
构造题使人神清气爽。
先判断无解的情况,如果 \(n \nmid \sum_{i=1}^n a_i\),那么显然无解。
做构造题不能只局限于样例给出的方法,因为它们一般都是特殊情况,而我们的目标是使用一般方法进行构造。
注意到无论怎么操作,元素的总和是不变的,我们可以尝试把所有值移动到一个元素上,然后因为 \(n \nmid \sum_{i=1}^n a_i\),所以一定能够用 \((n-1)\) 次操作平均分。
如果这么做,那么必然除了那个元素,其他的元素都是 \(0\)。对于一个操作 \((i,j,x)\),如果想让 \(a_i\) 置为 \(0\),那么必须满足 \(i \mid a_i\)。对于 \(i \nmid a_i\) 的情况,则要事先将 \(a_i\) 增大到满足 \(i \mid a_i\)。
增大多少呢?\(i-a_i \bmod i\),这是显然的。如果操作是 \((j,i,x)\),想让 \(a_i\) 变大,其值必定是 \(j\) 的倍数。为了能够增加任意值,钦定都移动到 \(a_1\) 上。
那么对于 \(i \nmid a_i\),必定先使用 \((1,i,k)\),其中 \(k = i - a_i \bmod i\)。那么将 \(a_i\) 清零,使用 \((i,1,\frac{a_i}{i})\)。如此至多使用 \(2 \cdot (n-1)\) 次操作,再使用 \((n-1)\) 次 \((1,i,ave)\),其中 \(ave = \frac{\sum_{i=1}^n a_i}{n}\)。总操作数 \(3 \cdot (n-1)\),满足条件。
有一个疑问,\(a_i\) 难道不会在残酷的修改中变为负数吗?假如 \(a_2\) 就需要进行增加操作,必然 \(a_i\) 要减小一个值。但是这个值是 \(2 - a_2 \bmod 2\),由于 \(2 \nmid a_2\),所以这个值一定是 \(1\)。而 \(a_i \in [1,10^5]\),所以绝对不是负数。
而后至少要增加 \(2\)(将 \(a_2\) 置 \(0\)),所以当修改 \(a_3\) 时,\(a_1\) 至少是 \(3\),如果要让 \(3 \mid a_3\),那么 \(a_1\) 减少的量又一定小于 \(3\),接着加上至少 \(3\)。如此类推,不会出现小于 \(0\) 的情况。
CODE
#include<bits/stdc++.h>
using namespace std;
const int N=1e4+5;
int T, n, a[N];
struct node { int i, j, x; };
vector<node> ans;
#define pb push_back
int read() {
int a=0; char c=getchar();
while(!isdigit(c)) c=getchar();
while(isdigit(c)) a=a*10+c-'0', c=getchar();
return a;
}
void solve() {
long long sum=0;
ans.clear();
n=read();
for(int i=1;i<=n;++i) sum+=(a[i]=read());
if(sum%n) { puts("-1"); return; }
int ave=sum/n;
for(int i=2;i<=n;++i) {
int k=a[i]%i;
if(k) k=i-k;
ans.pb({1,i,k});
a[1]-=k, a[i]+=k;
// 如果k=0,这一步可以没有,但是下一步必须要有
ans.pb({i,1,a[i]/i});
a[1]+=a[i], a[i]=0;
}
for(int i=2;i<=n;++i) ans.pb({1,i,ave});
printf("%d\n",ans.size());
for(auto a:ans) printf("%d %d %d\n",a.i,a.j,a.x);
}
int main() {
T=read();
while(T--) solve();
}