Codeforces Round 148 div2

titi题意:

对于一个数列,一共可以进行k次操作,每次操作有两种选择,要么删掉最小的两个,要么删掉最大的一个,求最后最多还有多少 

思路:一开始想用双向队列,优先队列,但是好像操作要么是自身stl库没有,要么就是实现起来复杂度太高,放弃

          其实可以将k看成一个和,前面后面各分其中1part,然后得到剩余的,那么其实只要枚举分的方式,然后对于每种方式求出其对应的值更新答案

           求值怎么求呢,可以考虑用前缀和优化,删去前面两个,就是减去sum[i*2],删去最后就是直接把被减数的sum往前挪就好了

#include<iostream>
#include<cstring>
#include<algorithm>
#define int long long
using namespace std;
const int N=2e5+10;
int a[N];
int t;
int sum[N];
int tot;
int n,k;
int ans;
signed main()
{
	cin>>t;
	while(t--)
	{
		cin>>n>>k;
		tot=0;
		ans=0;
		for(int i=1;i<=n;i++)
		{
			cin>>a[i];
		
		}
		
		sort(a+1,a+n+1);
		for(int i=1;i<=n;i++)
		{
			sum[i]=sum[i-1]+a[i];
		}
		
		for(int i=0;i<=k;i++)
		{
			if(2*i+k-i>n)continue;
			ans=max(ans,sum[n-(k-i)]-sum[i*2]);
		}
		cout<<ans<<endl;
	}
}

 思路:找峰值谷值就好啦

怎么找其实还是挺容易写错的:对于和我自己相同的当成一个就好了,然后根据当前我是处于增加,还是减少,设置标志,进行向下的循环,以我标志的状态和接下去一个与我的状态是否符合作为标准,直到出现下一个转折点,然后把ans++,同时记得i=j-1,因为马上i++;

#include<iostream>
#include<cstring>
#include<cstdio>
#include<algorithm>

using namespace std;
const int N=5e6+10;
int a[N];
int t,n;
int cot;
int ans;
bool check1(int j)
{
	return a[j]<=a[j+1];
}
bool check2(int j)
{
	return a[j]>=a[j+1];
}
int main()
{
	cin>>t;
	while(t--)
	{
		cin>>n;
		cot=0;
		ans=0;
		int flag=3;
		int fflag=0;
		
		for(int i=0;i<n;i++)
		{
			cin>>a[i];
			if(i&&a[i]!=a[i-1])fflag=1;
		}
		
		for(int i=0;i<n;i++)
		{
			int j=i;
			while(j+1<n&&a[j+1]==a[i])j++;
			if(j+1<n)
			{
				if(a[j]<a[j+1])flag=1;
				else flag=0;
			j++;
			while(j+1<n&&(flag?check1(j):check2(j)))j++;
			ans+=1;
			i=j-1;
		    }
		    else break;
		 } 
		 if(fflag==0)cout<<1<<endl;
		 else cout<<ans+1<<endl;		 
	
	}
}

 题意:给了一个数列,然后呢大家一开始都只能进行增加操作,然后是减少增加操作交替进行,一共有k个数,(1-k)可以对这个数列进行合理的增加和减少,问k此操作后,所有可能结果中,最大的最小值是多少

分析:那么肯定使用二分,二分一个最小值,如果可以构造出来一个数列使得其所有值都大于等于这个mid,那么就可以继续探索大于mid的了

          那么判断函数怎么写呢

          首先获取在这个数列中小于mid,需要进行增加操作的个数

          1.个数大于k,必然不可能   个数等于零,直接正确

          2.判断到底可不可以,我们先对整个序列进行了排序,然后遵循着小的增加大的,也即是(a[0]+k,a[1]+k-1...),只要判断最小的a[p]-p+k是否大于mid,就可以知道成不成立,这个可以通过预处理得出

          2.个数等于n,就是说所有都小,同时剩下来的k的操作数是个奇数,那么根据先减再增,必然失败

          3.因为大于mid其本身就ok,尽量减少对他们的操作,先让那些小的来消耗掉k,怎么消耗呢?我们是把这个数列进行排序,部分经过+k后会大于mid  那么接下去可以通过-1 (+1-2),来进行消耗,也就是每一个可以消耗掉   1+(k+a[i]-i-mid)*2

          这个式子对于所有的小于mid的求和,就是剩余的k为:k-cnt-2*((k-mid)*(cnt)+sum[cnt-1])

          sum是预处理出来的前缀和

        4.经过上一步之后,如果还有剩余的k,如果k是个奇数,那么进行+-+必然可以,或者剩余两个数进行操作,那么进行分担后也必然可以

         5.否则就判断那个唯一一个进行k此操作后是否>=mid

#include<iostream>
#include<cstring>
#include<cstdio>
#include<algorithm>
#include<vector>
#define int long long
using namespace std;
const int N=1e8;
vector<int>a,pre,mn;
typedef long long ll;
int n,q,k;
long long remain;

signed main()
{

    cin >> n >> q;
    vector<int> a(n);
    for(int i = 0; i < n; i++) cin >> a[i];
    sort(a.begin(), a.end());
    auto mn = a;
    for(int i = 0; i < n; i++) mn[i] -= i;
    auto pre = mn;
    for(int i = 1; i < n; i++) pre[i] += pre[i - 1];
    for(int i = 1; i < n; i++) mn[i] = min(mn[i], mn[i - 1]);
	
	  auto check = [&](int mid){
            int cnt = lower_bound(a.begin(), a.end(), mid) - a.begin();
            if (cnt == 0) return true;
            if (cnt > k) return false;
            if (cnt == n && (k - cnt) % 2) return false;
            if (mn[cnt - 1] + k < mid) return false;
            if (cnt == n){
                int remain = k - cnt - 2 * (cnt * (k - mid) + pre[cnt - 1]);
                return remain <= 0;
            }
            int remain = k - cnt - 2 * (cnt * (k - mid) + pre[cnt - 1]);
            if (remain <= 0 || remain % 2 == 1) return true;
            if (n - cnt >= 2) return true;
            if (a.back() - remain / 2 >= mid) return true;
            return false;
        };
	
	while(q--)
	{
		cin>>k;
		if(n==1)
		{
			if(k%2==0)cout<<a[0]-k/2<<" ";
			else cout<<a[0]-k/2+k<<" ";
			continue;
		}
	  int l = 1, r = 1e10;
        while(l < r){
            int mid = (l + r + 1) / 2;
            if (check(mid)) l = mid;
            else r = mid - 1;
        }
        cout << r << ' ';
    }
    cout << '\n';
	}

            

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值