当前位置 : 主页 > 网页制作 > HTTP/TCP >

P3147 [USACO16OPEN]262144

来源:互联网 收集:自由互联 发布时间:2021-06-16
原题链接https://www.luogu.org/problem/P3147 建议先食用无毒弱化版:P3146 [USACO16OPEN]248 这个题与上面的无毒版不同的是:数据范围变得很大,所以我们再像上个题一样定义状态是不行的了~ 新状

原题链接  https://www.luogu.org/problem/P3147

建议先食用无毒弱化版:P3146 [USACO16OPEN]248

这个题与上面的无毒版不同的是:数据范围变得很大,所以我们再像上个题一样定义状态是不行的了~

新状态设置: f [ i ][ j ] 表示从 j 开始能合成 i 的区间长度;

状态转移方程:

状态转移稍有些麻烦,我们看个图推一下:

按照题目所求的最大合并数,那么我们能合并就合并,最后才能合并出最大的那个数!

所以我们可以将红蓝区间继续合并,就能合成 i ;

回想一下上面状态是怎么定义的,独立思考一下转移方程 。

我们将状态转移分成两步:转移 f 数组的状态,转移 f 数组的值;

转移 f 数组的值:

这个十分简单,我们 f 数组表示的是区间长度,那么转移后的区间长度就是红蓝两个小区间的长度和呗,so easy ~

转移 f 数组的状态:

第一维:考虑到我们的 i 是由两个 i-1 转移过来的,所以第一维比较好确定,就是 i-1;

第二维:第二维表示的是起点,我们需要找到红蓝两个区间的左端点就可以了

显然蓝色区间的左端点是 j,红色区间的左端点就是 j + 蓝色区间的长度 !

蓝色区间长度怎么搞啊???哎,我们 f 数组表示的就是区间长度唉~

So 蓝色区间的长度不就是 f [ i-1 ][ j ] ?(从 j 开始能合成 i-1 的区间长度),那么红色区间的左端点就游刃有余了,就是 j + f [ i-1 ][ j ],那么就大功告成了~

还有再明白一点:最后合成的大区间的左端点就是蓝色区间的左端点(貌似是废话)~

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

 

边界条件:

这个也不是很难想,当我们第 i 个数输入的是 x 的时候,就初始化一下从 i 开始能合成 x 的区间是 1 (就是还没有合成呗~)

 

OK,就这么点了,上代码喽 \(^o^)/~

#include<iostream>
#include<cstdio>
#include<queue>
#include<cmath>
using namespace std;
int read()
{
    char ch=getchar();
    int a=0,x=1;
    while(ch<0||ch>9)
    {
        if(ch==-) x=-x;
        ch=getchar();
    }
    while(ch>=0&&ch<=9)
    {
        a=(a<<1)+(a<<3)+(ch-0);
        ch=getchar();
    }
    return a*x;
}
int n,x,maxn;
int f[100][300000];               //f[i][j] 表示从j开始能合成i的区间长度是多少 
int main()
{
    n=read();
    for(int i=1;i<=n;i++)         
    {
        x=read();
        f[x][i]=1;                //初始化 
    }
    for(int i=2;i<=58;i++)        //枚举第一维,最大合成的数不超过58 
    {
        for(int j=1;j<=n;j++)     //枚举第二维 
        {
            if(f[i][j]==0)        //目前没合成
            {
                if(f[i-1][j]&&f[i-1][j+f[i-1][j]])         //可以合成 
                {
                    f[i][j]=f[i-1][j]+f[i-1][j+f[i-1][j]]; //那就合成
                    maxn=i;       //肯定是越来越大的 
                }
            }           
        }
    }
    printf("%d",maxn);
    return 0;
}

再再再解释一个神奇的东东,有的童鞋可能会问第一维那个 58 是什么鬼;

其实就跟数据范围有关:

我们看到 2 ≤ N ≤ 262144,而我们像倍增一样合并,那么因为218 = 262144

而数字的大小在 1-40 之间,那么产生的数最多也就是 40+18=58 啦~

网友评论