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();
}

CF1416B Make Them Equal 题解
https://yozora0908.github.io/2022/cf1416b-solution/
作者
yozora0908
发布于
2022年7月1日
许可协议