zoukankan      html  css  js  c++  java
  • AtCoder Beginner Contest 143 F

    题意

    给出一个长度为NN的序列,求对于所有k[1,N]kin[1,N],每次从序列中选出kk个互不相同的数,最多能取多少次。
    N3e5Nle3e5

    题解

    我们首先把数组转化为相同的数的出现次数的序列,如序列(1,3,4,4)(1,3,4,4)就转化为(1,1,2)(1,1,2)。把这个得到的序列计作aa

    然后二分答案,假设当前二分到xx,能取xx次的条件是:
    (aixai)+xai>x1kxlarge(sum_{a_ile x}a_i)+xcdotsum_{a_i>x}1ge kcdot x因为同一个数最多取到xx次,而取到的总数为kxkcdot x

    发现当kk增大时答案减小,所以可以把二分去掉,写一个指针动就行了。这样的时间复杂度是O(nlogn)O(nlog n)的,因为要upper_boundupper\_bound

    CODE

    #include <bits/stdc++.h>
    using namespace std;
    inline void rd(int &x) {
    	char ch; for(;!isdigit(ch=getchar()););
    	for(x=ch-'0';isdigit(ch=getchar());)x=x*10+ch-'0';
    }
    typedef long long LL;
    const int MAXN = 300005;
    int N, n, a[MAXN], cnt[MAXN];
    LL sum[MAXN];
    int ans[MAXN];
    
    inline bool chk(int k, int x) {
    	int pos = upper_bound(a + 1, a + n + 1, x) - a;
    	return sum[pos-1] + 1ll*(n-pos+1)*x >= 1ll*k*x;
    }
    
    int main() {
    	rd(N);
    	for(int i = 1, x; i <= N; ++i) rd(x), ++cnt[x];
    	for(int i = 1; i <= 300000; ++i) if(cnt[i]) a[++n] = cnt[i];
    	sort(a + 1, a + n + 1);
    	for(int i = 1; i <= n; ++i) sum[i] = sum[i-1] + a[i];
    	int now = 0;
    	for(int k = n; k >= 1; --k) {
    		while(now < N && chk(k, now+1)) ++now;
    		ans[k] = now;
    	}
    	for(int i = 1; i <= N; ++i) printf("%d
    ", ans[i]);
    }
    

    然而看了别人的博客后发现,可以O(n)O(n)
    上代码:

    sum[x]sum[x]存的就是上面的不等式的左边部分。

    #include <bits/stdc++.h>
    using namespace std;
    inline void rd(int &x) {
    	char ch; for(;!isdigit(ch=getchar()););
    	for(x=ch-'0';isdigit(ch=getchar());)x=x*10+ch-'0';
    }
    typedef long long LL;
    const int MAXN = 300005;
    int n, cnt[MAXN];
    LL sum[MAXN];
    int ans[MAXN];
    
    inline bool chk(int k, int x) { return sum[x] >= 1ll*k*x; }
    
    int main() {
    	rd(n);
    	for(int i = 1, x; i <= n; ++i) rd(x), ++cnt[x], ++sum[cnt[x]];
    	for(int i = 1; i <= n; ++i) sum[i] += sum[i-1];
    	int now = 0;
    	for(int k = n; k >= 1; --k) {
    		while(now < n && chk(k, now+1)) ++now;
    		ans[k] = now;
    	}
    	for(int i = 1; i <= n; ++i) printf("%d
    ", ans[i]);
    }
    

    正确性读者自证不难

  • 相关阅读:
    A
    E
    C
    A
    exgcd
    博客
    简单数论
    extended_gcd(扩展欧几里德算法) 青蛙的约会
    扩展欧几里德算法—求解不定方程,线性同余方程
    素数筛 E
  • 原文地址:https://www.cnblogs.com/Orz-IE/p/12039197.html
Copyright © 2011-2022 走看看