zoukankan      html  css  js  c++  java
  • AT5160[AGC037C]Numbers on a Circle【贪心,堆】

    正题

    题目链接:https://www.luogu.com.cn/problem/AT5160


    题目大意

    给出两个长度为\(n\)的环序列\(a\)\(b\),每次你可以让\(a\)中的一个数变为它和相邻两个的和。

    求最少的步数将\(a\)变为\(b\)

    \(1\leq n\leq 10^5,1\leq a_i,b_i\leq 10^9\)


    解题思路

    被蓝题D烂了。

    考虑反过来做,每次操作为\(B_i=B_i-B_{i-1}-B_{i+1}\)

    然后考虑怎么搞这个东西,注意到\(B_i\)不能够减到负数所以同理也不能减到小于\(A_i\),考虑如果对于一个\(B_i\geq B_{i-1}+B_{i+1}\)那么此时位置\(i-1\)\(i+1\)显然都是不能动的,那么我们动\(B_{i}\)就一定是最优的。

    还有如果\(B_i=A_i\)时那么\(B_i\)就是不可以再操作的了。

    具体地我们每次找到最大的可操作的\(B_i\),然后把它减到不能再减即可,然后过程中判断是否无解即可。

    由于每次至少减半所以每个数应该会操作最多\(log\)次。

    时间复杂度:\(O(n\log n\log w)\)


    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;
    }
    
  • 相关阅读:
    scanf的参数类型自动转换
    Gedit中文乱码
    在VirtualBox的Ubuntu虚拟机中与母体Windows共享文件夹
    Win7安vc2008编译报LINK : fatal error LNK1000: Internal error during IncrBuildImage
    贝叶斯后验概率小记
    美国计算机专业最好的前20名学校
    Linux磁盘空间不够怎么办?
    Debian6.0装机过程
    把用户加到sudoers组中的方法
    vi命令手册
  • 原文地址:https://www.cnblogs.com/QuantAsk/p/15471734.html
Copyright © 2011-2022 走看看