• CF1416ESplit【dp,set】


    正题

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


    题目大意

    给出\(n\)个正整数的一个序列\(a_i\),你要把\(a_i\)拆成两个正整数的和\(b_{2i},b_{2i+1}\),要求使得\(b\)的相同连续段最少。

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


    解题思路

    考虑求最大的相邻相同数目,先考虑暴力的\(dp\),设\(f_{i,j}\)表示分解完\(a_i\)\(b_{2i+1}=j\)时的方案,那么有转移方程

    \[f_{i,j}=\max\{f_{i-1,k}+[k=a_i-j]\}+[2j=a_i] \]

    而且不难发现对于一个\(i\)来说它的所有\(f_{i,j}\)在加上\([2j=a_i]\)之前差距不会超过\(1\),而且我们显然只有可能从最大值转移。

    对于\(2j=a_i\)的情况很难处理,我们可以先考虑都是奇数的情况。

    首先开始都有\(f_{1,j}=0\),可以记为区间\([1,a_{1}-1]\),然后到第二个对于一个最大的\(j\),我们可以转移到\(a_2-j\)(如果合法)。同样的我们可以翻转之后得到一个新的最大区间\([l,r]\),当某次之后这个区间空了那么因为上面提到的\(f_{i,j}\)的差距不会超过\(1\),所以最大值不变然后区间变回\([1,a_{i}-1]\)

    之后考虑\(a_i\)有偶数的情况怎么处理,此时会出现的问题就是:如果\(\frac{a_i}{2}\)加之前是最大值,那么加上之后就变为了唯一的最大值,这个很好处理,而如果之前不是最大值,那么加了之后就变为了最大值。

    这个时候有可能会在区间之外出现一些单点的最大值,我们可以用\(set\)来储存这些位置,至于翻转之后所有的位置\(x\)都会变为\(a_i-x\),那么可以储存一个\(x\)表示实际上这个位置的值为\(x\times f+buf\)的情况,这样我们就可以快速的翻转然后把不合法的值去掉就好了。

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


    code

    #include<cstdio>
    #include<cstring>
    #include<algorithm>
    #include<set>
    #define ll long long
    using namespace std;
    const ll N=5e5+10;
    ll T,n,ans,l,r,flag,f,buf,a[N];
    set<ll> s;
    void solve(ll lim){
    	flag=1;
    	if(l<=r){
    		if(lim<=l)l=1,r=0;
    		else l=lim-l,r=lim-min(r,lim-1),swap(l,r),flag=0;
    	}
    	f=f*-1;buf=lim-buf;
    	while(!s.empty()){
    		ll w=(*s.begin())*f+buf;
    		if(w<1||w>=lim)s.erase(s.begin());
    		else break;
    	}
    	while(!s.empty()){
    		ll w=(*(--s.end()))*f+buf;
    		if(w<1||w>=lim)s.erase(--s.end());
    		else break;
    	}
    	return;
    }
    signed main()
    {
    	scanf("%lld",&T);
    	while(T--){
    		s.clear();f=ans=1;buf=flag=0;
    		scanf("%lld",&n);
    		for(ll i=1;i<=n;i++)
    			scanf("%lld",&a[i]);
    		if(a[1]&1)l=1,r=a[1]-1,ans++;
    		else l=r=a[1]/2;
    		for(ll i=2;i<=n;i++){
    //			if(s.size())printf("%d\n",*s.begin());
     			if(a[i]&1){
    				solve(a[i]);ans++; 
    				if(s.empty()&&flag)l=1,r=a[i]-1,ans++;
    			}
    			else{
    				if(s.find((a[i]/2-buf)*f)!=s.end()||a[i]/2>=l&&a[i]/2<=r)
    					s.clear(),l=r=a[i]/2;
    				else solve(a[i]),s.insert((a[i]/2-buf)*f),ans++;
    			}
    		}
    		printf("%lld\n",ans);
    	}
    	return 0;
    }
    
  • 相关阅读:
    Node.js之使用Buffer类处理二进制数据
    node.js之require
    node.js之模块
    node.js之setTimeout()、clearTimeout()与 setInterval()与clearInterval()
    Node.js之包与npm包管理工具
    node.js基础知识
    运维之linux基础知识(一)
    node.js之调试器
    Ubuntu 18.04安装搜狗输入法
    微信小程序项目总结记账小程序(包括后端)
  • 原文地址:https://www.cnblogs.com/QuantAsk/p/15557928.html
Copyright © 2020-2023  润新知