[luogu1108&2687][USACO4.3]Buy Low, Buy Lower(STL乱搞+高精度)

19 篇文章 0 订阅
7 篇文章 0 订阅

题目:

我是超链接

题解:

水题而已,大部分人都应该会做,我突然想挑战一下 nlogn 的做法,然后。。。。
b[i]表示以i结尾的方案数
二分的第一问好做,问题是方案数怎么统计
每一个g存的数字都会被一个大于ta的数字替换,也就是说假如把所有的g值记录下来就是单调递增的,这样每次更新g的时候,方案数就是前一位比他大的数字方案数的和。
但很快博主就发现这个思路超难实现:无法处理有相等的数字出现时的方案数,你既不能置之不理【按照以前的那个加,这不就重复了吗】,也不能全盘替换【把以前的清空,虽然最后一个最优,但是前面的以其他数字结尾的方案也全没了】,总之弄一个ans统计简直没法玩。
www那怎么办,博主看到了一个做法,把n++,最后一个a放一个极小的数字,这样就可以把所有的方案数总结起来。
woc好科学啊,我怎么没有想到?
这道题相比那个大家熟悉的还需要高精度,这里博主压了4位

代码:

#include <cstdio>
#include <vector>
#include <cstring>
#include <iostream>
using namespace std;
struct hh{int tim,z;};
vector<hh>nxt[5005];
int n,i,a[5005],g[5005],ans,ll[5005];
struct bignum{int l,a[500];void cl(){memset(a,0,sizeof(a));l=0;}}b[5005];
void gjj(bignum &a,bignum b)
{
    a.l+=b.l;int x=0;
    for (int i=1;i<=a.l;i++)
    {
        a.a[i]=a.a[i]+b.a[i]+x;
        x=a.a[i]/10000;
        a.a[i]%=10000;
    }
    while (!a.a[a.l]) a.l--;
}
int main()
{
    scanf("%d",&n);
    for (i=1;i<=n;i++) scanf("%d",&a[i]);
    int tot=0,maxx=0;
    a[++n]=-1e9;
    for (i=1;i<=n;i++)
    {
        int l=1,r=tot;
        while (l<=r)
        {
            int mid=(l+r)>>1;
            if (g[mid]<=a[i]) r=mid-1;
            else l=mid+1;
        }
        int now=i;
        if (!nxt[r+1].size() || a[i]!=a[nxt[r+1][nxt[r+1].size()-1].tim])//避免加入重复的数字 
          nxt[r+1].push_back((hh){i,a[i]});
        else now=nxt[r+1][nxt[r+1].size()-1].tim;
        b[now].cl();//把以前的清零,因为作为结尾的话后面的数字一定比前面的优 

        if (r)
        for (int j=ll[r];j<nxt[r].size();j++)
        {
            if (nxt[r][j].z<=a[i]) ll[r]++;
            else gjj(b[now],b[nxt[r][j].tim]);
        }
        g[r+1]=a[i];
        tot=max(tot,r+1);
        if (b[now].l==0) b[now].l=1,b[now].a[1]=1; 
        //处理方式就是以前重复过的就覆盖以前的,因为最后手动加了一个最小数,以前所有的方案都会汇总到一起 
    }
    printf("%d ",tot-1);
    //高精度压4位
    printf("%d",b[n].a[b[n].l]);
    for (i=b[n].l-1;i>=1;i--)
    {
        printf("%d",b[n].a[i]/1000);
        printf("%d",b[n].a[i]/100%10);
        printf("%d",b[n].a[i]/10%10);
        printf("%d",b[n].a[i]%10);
    }
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值