正题
题目链接:https://www.luogu.com.cn/problem/AT5160
题目大意
给出两个长度为nnn的环序列aaa和bbb,每次你可以让aaa中的一个数变为它和相邻两个的和。
求最少的步数将aaa变为bbb。
1≤n≤105,1≤ai,bi≤1091\leq n\leq 10^5,1\leq a_i,b_i\leq 10^91≤n≤105,1≤ai,bi≤109
解题思路
被蓝题D烂了。
考虑反过来做,每次操作为Bi=Bi−Bi−1−Bi+1B_i=B_i-B_{i-1}-B_{i+1}Bi=Bi−Bi−1−Bi+1。
然后考虑怎么搞这个东西,注意到BiB_iBi不能够减到负数所以同理也不能减到小于AiA_iAi,考虑如果对于一个Bi≥Bi−1+Bi+1B_i\geq B_{i-1}+B_{i+1}Bi≥Bi−1+Bi+1那么此时位置i−1i-1i−1和i+1i+1i+1显然都是不能动的,那么我们动BiB_{i}Bi就一定是最优的。
还有如果Bi=AiB_i=A_iBi=Ai时那么BiB_iBi就是不可以再操作的了。
具体地我们每次找到最大的可操作的BiB_iBi,然后把它减到不能再减即可,然后过程中判断是否无解即可。
由于每次至少减半所以每个数应该会操作最多logloglog次。
时间复杂度:O(nlognlogw)O(n\log n\log w)O(nlognlogw)
code
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<queue>
#define ll long long
#define mp(x,y) make_pair(x,y)
using namespace std;
const ll N=2e5+10;
ll n,a[N],b[N],ans;
priority_queue<pair<int,int> > q;
ll pl(ll x){return (x==1)?n:(x-1);}
ll pr(ll x){return (x==n)?1:(x+1);}
signed main()
{scanf("%lld",&n);for(ll i=1;i<=n;i++)scanf("%lld",&a[i]);for(ll i=1;i<=n;i++){scanf("%lld",&b[i]),q.push(mp(b[i],i));if(a[i]>b[i])return puts("-1")&0;}while(!q.empty()){ll x=q.top().second;q.pop();if(b[x]==a[x])continue;ll d=b[pl(x)]+b[pr(x)];if(b[x]-d<a[x])break;ans+=(b[x]-a[x])/d;b[x]-=(b[x]-a[x])/d*d;if(b[x]!=a[x])q.push(mp(b[x],x));}if(q.size())puts("-1");else printf("%lld\n",ans);return 0;
}