zoukankan      html  css  js  c++  java
  • 题解 CF1349B Orac and Medians

    题目链接

    我们可以把序列中的数,分为:小于(k)的,等于(k)的,和大于(k)的,三类。

    首先,如果等于(k)的数数量为(0),则答案一定是( ext{no})。因为中位数只会在原有的数中产生,不会凭空冒出来一个没有出现过的值。

    我们强行认为,我们的最后一次操作,是对整个序列求中位数(也就是操作区间为([l=1,r=n]))。之所以可以这样“强行认为”,是因为即使你已经用别的方法把序列变成了(a_1=a_2=dots=a_n=k),再来一次这样的最后操作也不会影响答案。

    于是,钦定了最后一次操作是对整个序列求中位数后,我们的问题转化为:要使得序列里所有数,排好序后,中间位置的值刚好是(k)

    考虑初始时的序列。如果(排好序后)中间位置已经是(k),则不用再操作了。否则,会有两种情况:

    • (>k)的数太多了。也就是说现在在中间位置上的,是一个(>k)的数。
    • (<k)的数太多了。也就是说现在在中间位置上的,是一个(<k)的数。

    对于第一种情况。首先,序列里一定不全是(>k)的数(因为通过特判,我们保证了序列里至少有一个(=k)的数)。那么,就一定存在一对相邻的数,满足一个(>k),另一个(leq k)。我们每次对这样一对相邻的数操作,就能把那个(>k)的数换成(leq k)的数,也就是每次操作能使(>k)的数数量恰好减少(1)。那么,通过若干次操作,就一定能让整个序列(排好序后)中间位置上的值刚好是(k)。因此,第一种情况答案一定是( ext{yes})

    对于第二种情况。我们要想办法通过一些操作将其转化为第一种情况。也就是说,要尽量把(<k)的数变成(geq k)的数。发现每次操作,需要区间里(geq k)的数的数量严格大于区间里(<k)的数的数量,我们才能把(<k)的数同化(变成(geq k)的数)。那么,用和上面一样的方法,每次合并相邻两个数,就不行了。考虑如果序列里存在连续的长度至少为(2)的一段数,满足他们全部(geq k),则可以从这一段数出发,不断把两侧的数同化(变成(geq k)的)。于是问题转化为如何使序列里至少有一段长度(geq 2)的全部(geq k)的数。

    考虑求一个数组(f[i]),表示(1dots i)中,(geq k)的数的数量减去(<k)的数的数量的差。如果能找到一对((i,j)),满足(i-jgeq 2)(f[i]-f[j]>0)(也就是说区间([j+1,i])(geq k)的数量严格大于(<k)的数量),那么,可以通过一次操作使得([j+1,i])中所有数全部(geq k)。这样就得到了上一段所说的:长度(geq 2)的,所有数都(geq k)的区间。于是从这个区间出发,可以把更多的数变成(geq k)的数,这又转化为了一定有解的第一种情况。所以,在第二种情况下,只要找到了这样的((i,j)),则答案一定是( ext{yes}),否则答案一定是( ext{no})。找((i,j)),只需要枚举(i),维护(jin [1dots i-2])中所有(f[j])的最小值即可。

    时间复杂度(O(n))

    参考代码:

    //problem:CF1349B
    #include <bits/stdc++.h>
    using namespace std;
    
    #define pb push_back
    #define mk make_pair
    #define lob lower_bound
    #define upb upper_bound
    #define fi first
    #define se second
    #define SZ(x) ((int)(x).size())
    
    typedef unsigned int uint;
    typedef long long ll;
    typedef unsigned long long ull;
    typedef pair<int,int> pii;
    
    const int MAXN=1e5;
    int n,K,a[MAXN+5],f[MAXN+5];
    
    int main() {
    	int T;cin>>T;while(T--){
    		cin>>n>>K;
    		int cntgreater=0,cntK=0;
    		for(int i=1;i<=n;++i){
    			cin>>a[i];
    			if(a[i]==K)cntK++;
    			else if(a[i]>K)cntgreater++;
    		}
    		if(!cntK){
    			cout<<"no"<<endl;continue;
    		}
    		if(cntK+cntgreater>=n/2+1){
    			cout<<"yes"<<endl;continue;
    		}
    		int mn=n;
    		bool yes=false;
    		for(int i=1;i<=n;++i){
    			f[i]=f[i-1];
    			if(a[i]>=K)f[i]++;
    			else f[i]--;
    			if(f[i]-mn>0){
    				yes=true;
    				break;
    			}
    			mn=min(mn,f[i-1]);
    		}
    		if(yes)cout<<"yes"<<endl;
    		else cout<<"no"<<endl;
    	}
    	return 0;
    }
    
  • 相关阅读:
    Linux Ubuntu 忘记用户名和密码 解决办法
    C语言中标准输入流、标准输出流、标准错误输出流
    递归实现字符串反转char* reverse(char* str)合集
    (转)最好的求平方根的方法(精确度VS速度)Best Square Root Method Algorithm Function (Precision VS Speed)
    java验证码识别4
    互聯網產品設計主題詞表
    java验证码识别3
    C++实现C#的get,set属性操作
    简陋,山寨,Everything,桌面搜索,原理,源码
    java验证码识别1
  • 原文地址:https://www.cnblogs.com/dysyn1314/p/12881386.html
Copyright © 2011-2022 走看看