• 【CF786C】Till I Collapse


    题目

    题目链接:https://codeforces.com/problemset/problem/786/C
    (n) 个数划分成 (m) 段使得每中不同数字的个数 (le k)。对于每个 (k) 满足 (1le kle n) 分别求出最小的 (m)
    (nleq 10^5,1leq a_ileq n)

    思路

    显然每次贪心选择是最优的。由于总分段次数的上界是 (sum^{n}_{i=1}frac{n}{i}=O(nlog n)) 的,所以我们可以考虑每次找出分段的右边界。
    假设上一次分段的位置是 (p),我们预处理出 (pre[i]) 表示位置 (i) 上的数字上一个和它相同的数字在哪里。那么我们只需要找到第 (p+k+1) 个满足 (pre[i]leq i) 的数字位置,让 (p) 跳到这个位置减一即可。
    采用主席树就可以轻松解决。时间复杂度 (O(nlog^2 n))

    代码

    #include <bits/stdc++.h>
    using namespace std;
    
    const int N=100010,LG=18;
    int n,a[N],pre[N],last[N],rt[N];
    vector<int> pos[N];
    
    struct SegTree
    {
    	int tot,lc[N*LG*4],rc[N*LG*4],cnt[N*LG*4];
    	
    	int update(int now,int x,int l,int r,int k)
    	{
    		if (!x || x==now) x=++tot,lc[x]=lc[now],rc[x]=rc[now],cnt[x]=cnt[now]+1;
    			else cnt[x]++;
    		if (l==r) return x;
    		int mid=(l+r)>>1;
    		if (k<=mid) lc[x]=update(lc[now],lc[x],l,mid,k);
    			else rc[x]=update(rc[now],rc[x],mid+1,r,k);
    		return x;
    	}
    	
    	int query(int x,int l,int r,int k)
    	{
    		if (l==r) return l;
    		int mid=(l+r)>>1;
    		if (cnt[lc[x]]>=k) return query(lc[x],l,mid,k);
    			else return query(rc[x],mid+1,r,k-cnt[lc[x]]);
    	}
    }seg;
    
    int main()
    {
    	scanf("%d",&n);
    	for (int i=1;i<=n;i++)
    	{
    		scanf("%d",&a[i]);
    		pre[i]=last[a[i]]; last[a[i]]=i;
    		pos[pre[i]].push_back(i);
    	}
    	pos[0].push_back(n+1);
    	for (int i=0;i<=n;i++)
    	{
    		for (int j=0;j<pos[i].size();j++)
    			rt[i]=seg.update(i?rt[i-1]:0,rt[i],1,n+1,pos[i][j]);
    		if (!rt[i]) rt[i]=rt[i-1];
    	}
    	for (int i=1;i<=n;i++)
    	{
    		int ans=0;
    		for (int j=0;j<n;ans++)
    			j=seg.query(rt[j],1,n+1,j+i+1)-1;
    		cout<<ans<<" ";
    	}
    	return 0;
    }
    
  • 相关阅读:
    汉语-词语:理解
    汉语-词语:头脑
    SELECT INTO
    SELECT
    scp
    samba
    rpmgraph
    rpmdeps
    rpmcache
    rpmbuild
  • 原文地址:https://www.cnblogs.com/stoorz/p/15003457.html
Copyright © 2020-2023  润新知