zoukankan      html  css  js  c++  java
  • CodeForces 786C Till I Collapse

    time limit per test2 seconds
    memory limit per test256 megabytes
    inputstandard input
    outputstandard output

    解题思路

    无法AC的暴力

    从1到n枚举k,对于每一个k,从左到右贪心扫一遍序列,统计已有的颜色种类,如果种类数超过了就再分一组,将种类数置为1。扫完一遍就得到答案了。显然复杂度(O(n^2))过不了。于是有了两个方向的优化——枚举k太多了,减少枚举k的次数,“加剪枝”;对于固定的k,扫一遍太多了,对于每一个k,不老老实实扫序列,而是在序列上“跳着扫”。

    加剪枝

    剪枝是这样的——如果显然随着k的增加,答案是单调不下降的,所以如果(k==l)时和(k==r)时最少分组数量相同,即(ans[l]==ans[r]),那么k取值在([l,r])之间时,最少分组数量也是相同的。仿佛活在梦里哦……

    举个例子,输入的序列是自然数1 2 3 4 5 6 7 8 9 10,当(k==5)时,显然答案是2,当(k==9)时,显然答案也是2,那么当(k==6、k==7、k==8)时,答案不用算就是2。过于暴力。

    然后枚举k的方法类似线段树划分区间,看看k在最大时和最小时相不相等,不相等就二分,看k在最小时和中间值时相不相等,以此类推……

    复杂度不会分析,应该和序列的形态有关,还和二分方式有关。

    对于下面给的代码,本机(CPU为i7-8565U)实测,当输入长度为十万的自然数序列时,耗时1.3s,但是CF评测姬快,所以可以AC。

    跳着扫

    我们一步一步扫描序列时,当发现目前序列中颜色种数超过k,就分出新的一组。区间内的种数,然后联想到这题[SDOI2009]HH的项链(这篇博客貌似已经成为我的博客里被引用最多的一篇,当然只是自引),然后显然要在线做,于是单纯的权值线段树不够用了,上主席树,想到这篇HDU 5919 Sequence ll,每次从当前点开始向后跳k个1(把第一次出现的置为1),这样子每次不用扫描总共n步了,只用跳ans[k]步就好,然后我们知道ans[k]不是很大(意会),所以跑得快。

    时间复杂度嘛,我们扫k次,总共要跳(sum_{i=1}^{n} ans[i])次(这篇博客说ans[k]总和大概是(nlog n)级别),每次在主席树上找下一个点的复杂度是一个log,于是总的复杂度就是(O(nlog ^2n))

    我觉得这两种优化可以综合一下诶……

    源代码

    使用了第一种优化

    #include<stdio.h>
    #include<string.h>
    
    int n;
    int a[100010];
    int b[100010];//桶
    int ans[100010];
    int f(int k)
    {
    	memset(b,0,sizeof(b));
    	int cnt=1,id=b[a[1]]=1;//分组内的颜色数量和当前组编号
    	for(int i=1;i<=n;i++)
    	{
    		if(b[a[i]]==id) continue;//同一组中再次出现的颜色
    		b[a[i]]=id;//或者说之前分走了一组,这是新的一组,该组中第一次出现这个颜色
    		cnt++;//该组中颜色数量加一
    		if(cnt>k)//该分新的一组了
    		{
    			b[a[i]]=++id;
    			cnt=1;
    		}
    	}
    	return id;
    }
    
    void solve(int kl,int kr)
    {
    	ans[kl]=f(kl);
    	ans[kr]=f(kr);
    	if(ans[kl]==ans[kr])
    	{
    		for(int i=kl+1;i<kr;i++) ans[i]=ans[kl];
    		return;
    	}
    	int mid=kl+kr>>1;
    	solve(kl,mid);
    	solve(mid+1,kr);
    }
    
    int main()
    {
    	scanf("%d",&n);
    	for(int i=1;i<=n;i++) scanf("%d",a+i);
    	solve(1,n);
    	for(int i=1;i<=n;i++) printf("%d ",ans[i]);
    	return 0;
    }
    
    
  • 相关阅读:
    java小知识点5
    java小知识点4
    java小知识点3
    编程之法:面试和算法心得(寻找最小的k个数)
    389. Find the Difference
    104. Maximum Depth of Binary Tree
    485. Max Consecutive Ones
    693. Binary Number with Alternating Bits
    463. Island Perimeter
    566. Reshape the Matrix
  • 原文地址:https://www.cnblogs.com/wawcac-blog/p/11272967.html
Copyright © 2011-2022 走看看