HDU6049 Sdjpx Is Happy 枚举+ST表

发现做法和别人不同,那就写个题解 (另外本题是神大抄的GCJ题目,数据出的很弱,可以去gcj测一下Google Code Jam
(约定F(l,r)为区间[l,r]在不交换的情况下满足题意的K)
试想不能交换,那么我们可以O(n)求出F(1,n)。如果能交换一次,很显然,只能是 [l1,r1] [l2,r2] ,并且满足 Lil1r1<l2r2Ri,1iK ,因为这样才能对答案作出贡献。定义 f(i) 为第 i 段区间能交换一次的答案,这样将问题转换为求出maxKi=1(f(i))。对每段区间,仔细分析,可以发现必须满足 l1=L,r2=R,Rminr1i=l1=r1l1,maxr2i=l2L=r2l2 ,这样交换才能满足条件,所以我们可以O(n)的求出所有 r1,l2 ,再枚举 r1,l2 ,并求出 F(r1+1,l21) ,这样的时间复杂度为 O(n3) 。求 F(l,r) ,我们可以采用一种用st表快速跳转的方法,对于最慢的数据(如 3,4,5,6,7 ),是O(n)的,但是对于其余枚举的 r1,l2 都是O(1)的,所以总的时间复杂度为 O(n2)

#include <cstdio>
#include <cmath>
#include <iostream>

using namespace std;

const int maxn = 5010;

int n;
int st[maxn][32];
int lg2[maxn];
struct node
{
    int l,r;
}stk[maxn];
int a[maxn];
int p[maxn];
int f[maxn],s[maxn];

inline int max(int x,int y)
{
    return x > y ? x : y;
}

inline int min(int x,int y)
{
    return x < y ? x : y;
}

void init()
{
    lg2[1] = 0;
    for(int i = 2; i <= n; i++)
        lg2[i] = lg2[i-1] + (1 << (lg2[i-1] + 1) == i);
    int k = lg2[n] + 1;
    for(int i = 1; i <= n; i++)
        st[i][0] = a[i];
    for(int j = 1; j < k; j++)
        for(int i = 1; i <= n; i++)
            if(i + (1 << j) <= n + 1)
                st[i][j] = max(st[i][j-1],st[i+(1 << (j-1))][j-1]);
}

int q(int l,int r)
{
    int k = lg2[r - l + 1];
    return max(st[l][k],st[r - (1 << k) + 1][k]);
}

int L[maxn],cl;
int R[maxn],cr;

int work(int l,int r,int L)
{
    int res = 0;
    int j;
    for(int i = l; i <= r; i++)
    {
        j = L + i - l;
        if(a[i] != j)
        {
            int k = p[j];
            int qq;
            while(1)
            {
                qq = q(i,k);
                if(qq - j == k - i)
                    break;
                k = qq - j + i;
            }
            i = qq - j + i;
        }
        res++;
    }
    return res;
}

int calc(int l,int r)
{
    cl = cr = 0;
    int minn = 1e9;
    for(int i = l; i <= r; i++)
    {
        minn = min(a[i],minn);
        if(r - minn == i - l)
            L[++cl] = i;
    }
    int maxx = 0;
    for(int i = r; i >= l; i--)
    {
        maxx = max(a[i],maxx);
        if(maxx - l == r - i)
            R[++cr] = i;
    }
    int res = 0;
    for(int i = 1; i <= cl; i++)
    {
        for(int j = 1; j <= cr; j++)
        {
            if(L[i] >= R[j])
                break;
            int mid = work(L[i]+1,R[j]-1,r - R[j] + l + 1);
            res = max(res,mid+1);
        }
    }
    return res;
}

int main()
{
    int T;
    cin >> T;
    for(int cas = 1; cas <= T; cas++)
    {
        printf("Case #%d: ",cas);
        int cnt = 0;
        int ans = 0;
        int ppp;
        scanf("%d",&n);
        for(int i = 1; i <= n; i++)
        {
            scanf("%d",&a[i]);
            f[i] = 0;
            p[a[i]] = i;
        }
        init();
        for(int i = 1; i <= n; i++)
        {
            int l = i;
            if(a[i] != i)
            {
                int j = p[i];
                while(1)
                {
                    int qq = q(l,j);
                    if(qq == j)
                        break;
                    j = qq;
                }
                i = j;
            }
            stk[++cnt].l = l;
            stk[cnt].r = i;
        }
        for(int i = 1; i <= cnt; i++)
        {
            if(stk[i].l == stk[i].r)
                continue;
            ans = max(ans,calc(stk[i].l,stk[i].r));
        }
        printf("%d\n",cnt+ans);
    }
    return 0;
}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值