0x1f 关于猜想
看完题目后,注意力好的同学可能会发现:有解的充要条件是所有所需的金属编号模 \(\gcd(A,B)\) 同余。
0x2f 关于证明:
根据题意,我们可以将 \(i\) 分解成 \(j\) 这个操作,看作是 \(i\) 经过若干次减 \(A\) 和若干次减 \(B\) 得到了 \(j\),即 \(i-Ax-By=j\)。
令 \(d=\gcd(A,B)\)。
必要性
我们知道,最终的答案 \(S\) 经过分解后能够得到所有我们所需要的金属。对于每一块所需的金属编号 \(M_i\),都存在自然数 \(x_i\) 和 \(y_i\) 使得 \(S-Ax_i-By_i=M_i\),变形可得 \(S-M_i=Ax_i+By_i\)。
由于 \(d\mid (Ax_i+By_i)\),所以 \(d\mid (S-M_i)\),即 \(S \equiv M_i (\bmod d)\)。
不难得出,所有的所需编号都模 \(d\) 同余,该命题得证。
充分性
先考虑只有两块金属 \(M_1\) 和 \(M_2\) 的情况:
我们知道若有解,就一定存在一块金属 \(S'\) 使得它能够分解成 \(M_1\) 和 \(M_2\),即 \(S'-Ax_1-By_1=M_1\) 且 \(S'-Ax_2-By_2=M_2\),为满足题目条件,我们就令 \(x_1,x_2,y_1,y_2 \in N_+\)。问题就是是否存在这样的 \(x_1,x_2,y_1,y_2\)。
我们将上面的两个公式稍加变形:
将两式合并。
因为 \(M_2 \equiv M_1 \mod d\)。所以 \(d\mid M_2-M_1\)。
我们令 \(M_2-M_1=kd\)。最终,公式就变成了 \(A(x_1-x_2)+B(y_1-y_2)=kd\)。
有了上面的推论,无解情况就很好判断了。那么其他的就是有解情况了。
我们可以保证其他情况都有解,且这个解与前面的所有所需编号模 \(\gcd(A,B)\) (即上文中的 \(d\))同余。因此,我们只要从 \(n\) 出发,枚举所有可能的 \(S\) 即可。
我们用 \(mp\) 来记录编号与现有的数量,由于分解得到的编号一定是从大到小的,所以我们将 \(mp\) 按键升序排列。从 \(S\) 出发,一直分解,当遍历到 \(j\) 时,它的数量已经是固定的,若 \(j\) 是我们最终需要的金属,先判断够不够,不够直接退出循环,够了则减去所需量,再进行分解。知道最后全部分解完后,\(mp\) 为空,结束遍历。
最后,我们遍历数组 \(now\),若全部金属都已足够,就可以输出答案结束循环了。
0x3f 关于代码
\(\huge \mathscr{Code}\)
#include<bits/stdc++.h>
using namespace std;
const int N = 1e5+100;
int T,n,m,A,B,z,U[N],now[N];
int main(){ios::sync_with_stdio(false);cin.tie(0),cout.tie(0);cin>>T;for(int _=1;_<=T;_++){cin>>n>>A>>B;int g = __gcd(A,B),flag = 1;m = -1;for(int i=1;i<=n;i++){cin>>U[i];if(U[i]){if(m==-1) m = i%g;else flag &= (i%g==m);}}if(!flag){cout<<"Case #"<<_<<": "<<"IMPOSSIBLE\n";continue;}int pos = n;while(true){for(int j=1;j<=n;j++) now[j] = U[j];map<int,int,greater<int>> mp;mp[pos] = 1;for(auto e:mp){int key = e.first,val = e.second;if(key<=n && now[key]){if(now[key]>val) break;else{val -= now[key];now[key] = 0;}}if(key>A) mp[key-A] += val;if(key>B) mp[key-B] += val;}bool flagg = 1;for(int j=1;j<=n;j++) flagg &= (now[j]==0);if(flagg){cout<<"Case #"<<_<<": "<<pos<<'\n';break;}pos += g;}}return 0;
}