直接加感觉不可做,于是考虑反过来,这样一次操作将 (B) 数组中一个数减去他左右数之和。
即将 (B_i) 变为 (B_i-B_{i-1}-B_{i+1})。
首先 (A_i>B_i) 直接无解。
再找切入点,相当于我们找转化后第一次操作,也就是原来的最后一次操作,由于每一个数必须是正整数,所以对于当前 (B) 数组中的最大值 (B_i),(B_{i-1}) 和 (B_{i+1}) 是不可以执行操作的,也就是说这两个数在对 (B_i) 执行操作前不能够改变。
这样即使 (B_{i-1}) 需要操作,也必定在 (B_i) 之后(转化后的问题)操作。所以我们先对最大值操作,操作结束后,我们发现这个序列还可以继续按上面的方式找出最大值,于是再找最大值,这个过程可以用堆维护。
- 每一步找出数组中最大值,对这个数执行操作直到不能再减为止。
注意如果把一个数减到了 (A_i),就弹出可以数找剩下里面的最大值,相当于这个数永远不需要再操作了,移出这个体系。
如果最大值已经没法再操作了,但是还是 ( eq A_i),就无解,因为能且只能影响它的左右位置的两个数一样没法变化。
这样不能保证时间复杂度,所以需要把多次操作一起执行,我们直接每次减去直到不能再减为止,因为如果还可以再减,(B_i>B_{i-1}+B_{i+1}),(B_{i-1}) 和 (B_{i+1}) 是还是不可以执行操作。
这样一个数每次一定至少减半。
时间复杂度 (O(nlog nlog W)),(W) 为值域。
using namespace std;
typedef long long LL;
LL n,ans = 0;
LL a[200005],b[200005];
LL c[200005];
struct node{
LL id,val;
// 注意元素同时要存在数组中的位置
bool operator < (const node &nd)const{
return nd.val > val;
}
}h,t;
priority_queue <node> q;
bool cmp(LL x,LL y){
return b[x] > b[y];
}
LL getnum(LL x){
if(x == 0) return n;
if(x == n + 1) return 1;
return x;
// 处理环问题
}
int main(){
ios::sync_with_stdio(false);
cin >> n;
for(LL i = 1;i <= n;i ++) cin >> a[i];
for(LL i = 1;i <= n;i ++) cin >> b[i];
for(LL i = 1;i <= n;i ++){
if(a[i] > b[i]){
cout << -1 << endl;
return 0;
}
} // 无解
for(LL i = 1;i <= n;i ++){
if(a[i] < b[i]){
t.id = i; t.val = b[i];
q.push(t);
}
}
while(!q.empty()){
LL del,mns,i = q.top().id; q.pop();
del = b[i] - a[i];
mns = b[getnum(i - 1)] + b[getnum(i + 1)];
if(del / mns == 0 && b[i] != a[i]){ cout << -1 << endl; return 0; }
// 无解
ans += del / mns;
b[i] -= (del / mns) * mns;
// 直接减完
if(b[i] > a[i]){
t.id = i; t.val = b[i];
q.push(t);
}
// 还没到 a[i],到了就不用再入队了
}
cout << ans << endl;
return 0;
}
原评分 (sf{color{blue}1961})
前置知识:堆
代码难度/实现难度:很低吧