zoukankan      html  css  js  c++  java
  • [atcoder caddi]E

    题目大意:

    给定(n)个正整数(a_i),每次可以将一个数乘以-2,求最小的操作次数使得最后的序列单调不降。

    思路:

    最后的序列一定是前面为负数,后面为正数。
    于是我们枚举正数负数的分割点,这样操作就只有乘4一种了,现在问题转化为用最小的操作次数将一段前缀变成单调不升和一段后缀变成单调不降的。
    前缀和后缀的情况类似,现在考虑前缀:不难发现每添加一个新的点(i),前面的数就要选择一截乘([j,i-1])乘4,也就是上升到新的数的上面,这个时候(a_{i-1}ge a_i)
    如果原来(a_{i-1}geq a_i),那么这个时候(a_i)便有一段上升的空间,以后后面的数要上升的时候可以到(a_i)这里就停了。
    于是发现我们要维护其实就是一个阶梯状的图案,每次将高度较小的抬升一段距离,这个直接用栈来维护即可。

    #include<bits/stdc++.h>
    
    #define REP(i,a,b) for(int i=a,i##_end_=b;i<=i##_end_;++i)
    #define DREP(i,a,b) for(int i=a,i##_end_=b;i>=i##_end_;--i)
    #define debug(x) cout<<#x<<"="<<x<<" "
    #define pii pair<ll,ll>
    #define fi first
    #define se second
    #define mk make_pair
    #define pb push_back
    typedef long long ll;
    
    using namespace std;
    
    void File(){
    	freopen("e.in","r",stdin);
    	freopen("e.out","w",stdout);
    }
    
    template<typename T>void read(T &_){
    	_=0; T f=1; char c=getchar();
    	for(;!isdigit(c);c=getchar())if(c=='-')f=-1;
    	for(;isdigit(c);c=getchar())_=(_<<1)+(_<<3)+(c^'0');
    	_*=f;
    }
    
    const int maxn=2e5+10;
    int n;
    ll a[maxn],f[maxn],g[maxn],ans=1e18;
    
    int main(){
    	File();
    	read(n);
    	REP(i,1,n)read(a[i]);
    
    	stack<pii>s1;
    	REP(i,2,n){
    		f[i]=f[i-1];
    		ll pre=a[i-1],now=a[i];
    		while(pre<a[i]){
    			pre*=4;
    			if(s1.empty())f[i]+=(i-1)*2;
    			else{
    				pii p=s1.top(); s1.pop();
    				f[i]+=(i-p.fi)*2;
    				if(--p.se)s1.push(p);
    			}
    		}
    		ll cnt=0;
    		while(now*4<=pre)now*=4,++cnt;
    		if(cnt)s1.push(mk(i,cnt));
    	}
    
    	reverse(a+1,a+n+1);
    
    	stack<pii>s2;
    	REP(i,2,n){
    		g[i]=g[i-1];
    		ll pre=a[i-1],now=a[i];
    		while(pre<a[i]){
    			pre*=4;
    			if(s2.empty())g[i]+=(i-1)*2;
    			else{
    				pii p=s2.top(); s2.pop();
    				g[i]+=(i-p.fi)*2;
    				if(--p.se)s2.push(p);
    			}
    		}
    		ll cnt=0;
    		while(now*4<=pre)now*=4,++cnt;
    		if(cnt)s2.push(mk(i,cnt));
    	}
    
    	reverse(g+1,g+n+1);
    
    	REP(i,0,n)ans=min(ans,f[i]+g[i+1]+i);
    
    	printf("%lld
    ",ans);
    
    	return 0;
    }
    
    
  • 相关阅读:
    一个在线的C++帮助文档网站
    linux 学习笔记 (四)
    类的static成员函数和const成员函数
    Linux的inode、软链接、硬链接
    常用linux命令(三)
    多语言调用之 C++ 调用 Java JNI
    多语言调用之 Java调用C/C++
    NHibernate 操作原生SQL以及查询DataTable,DataSet
    DataGridView控件用法合集
    Java AOP实战 寻找SQL的引用路径
  • 原文地址:https://www.cnblogs.com/ylsoi/p/10165209.html
Copyright © 2011-2022 走看看