• Luogu-P3205-HNOI2010-合唱队


    题目地址

    思路

    这道题其实是P3146 [USACO16OPEN]248的升级版,但是N的范围很大,为262144。原先的O(N3)的方法自然会TLE,甚至O(N2)的方法也不足以解决。

    定义f[i][j]为从左端点j开始,能合并出i的(右端点值+1)的值

    首先进行初始化,对于坐标为i点的值t,处理如下:
    他的左断点为i,能合并出的值为t,右端点为i,值赋予i+1
    为什么是i+1呢?因为我们之后合并两个数的时候,从f[i][j]去搜,可以直接将其作为左端点搜索下一个数。

    转移方程为:

    [f[i][j]=f[i-1][f[i-1][j]] ]

    为什么?
    (f[i-1][j])代表从端点j开始找能合并成i-1的最少距离,并找出右端点的下一个端点。
    (f[i-1][f[i-1][j]])就代表从上一个找到的右端点下个端点开始搜索。如果没有搜到,肯定返回时0,就说明没有找到。

    所以,如果当(f[i][j])不为0后,这个i就是最大合并的值。

    所以我们只要枚举i与j就可以了。
    j的范围是n,那i呢?

    我们知道,数最大是40,两个40合成一个41,四个40合成一个42,八个40合成一个43,十六个40合成一个44。

    假设数都是最大的40,可以得出2^x个数字可以合成最大的值为40+x。
    又因为N=262144,而且2^18=262144,所以最大的合成数为40+18=58,我们只要枚举到58就行了。

    代码示例

    #include<cstdio>
    using namespace std;
    int f[61][262145],n,x,ans;
    int main(){
    	scanf("%d",&n);
    	for(int i=1;i<=n;i++)scanf("%d",&x),f[x][i]=i+1;
    	for(int i=2;i<=58;i++){
    		for(int j=1;j<=n;j++){
    			if(!f[i][j])f[i][j]=f[i-1][f[i-1][j]];
    			if(f[i][j])ans=i;
    		}
    	}
    	printf("%d",ans);
    	return 0;
    }
    

    PS:当然,把数组开反也是可以的。

  • 相关阅读:
    第3章 机器学习的典型应用 3-2 典型应用-聚类
    第3章 机器学习的典型应用 3-1 典型应用-关联规则
    6-13 Hog特征1
    6-12 SVM小结
    Linux中常见的环境变量笔记
    Linux中常见的环境变量笔记
    Linux中shell变量基础概念笔记
    Linux中shell变量基础概念笔记
    Linux常用内建命令笔记
    Linux常用内建命令笔记
  • 原文地址:https://www.cnblogs.com/dmoransky/p/10742631.html
Copyright © 2020-2023  润新知