2015 HNU warm up 05

B.Circle of digits Input

给出一串数环(只包含数字‘1’~‘9’),把数环分成k部分,每个部分可以表达一个十进制数。令某种分割方法中,最大的数为该方法的特征值,求这个数环所能得到的最小特征值。


TLE,数据范围达到10^5。


令特征值最后可能得长度为len,那么len = (n + k  - 1) % k;

能想的的做法是,扩展数环为两倍的数串,做一遍后缀数组,这样可以得到每个后缀的排名。利用height数组,得到每个位置往后扩展len长度之后的排名。之后令每个位置为最大值,贪心扫一遍(尽量使分块长度为len,否则为len-1,并检查剩下部分是否合法)。

但是还是会t。


据说做法是:

"先处理所有串,i 可能 的下一个串的起始位置i+len 和 i+len-1,i作为父亲,后面小于i的串两个做为儿子然后构造一个森林,然后用LCT维护,然后可以用枚举答案,答案做根,末串只有两种,access判断这两种末串根到根的节点总数是不是k,这样可以在O(logn)的时间内判断一个答案是否可行,n次O(nlogn),然后建树是也是(nlogn)。" 

                                                                                                                                         ---- [hut]-Keam

不会。


暂且贴个t的。。

#include <algorithm>
#include <iostream>
#include <stdlib.h>
#include <string.h>
#include <stdio.h>
using namespace std;
#define MAXN 200010
#define INF 123456789

int sa[MAXN], sb[MAXN], r[MAXN];
int wa[MAXN], wb[MAXN], wc[MAXN], wd[MAXN];

int cmp(int *r, int a, int b, int l)
{
    return r[a] == r[b] && r[a + l] == r[b + l];
}

void da(int *r, int *sa, int n, int m)
{
    int *x = wa, *y = wb;
    for(int i = 0; i < m; i++) wd[i] = 0;
    for(int i = 0; i < n; i++) wd[x[i] = r[i]]++;
    for(int i = 1; i < m; i++) wd[i] += wd[i - 1];
    for(int i = n - 1; i >= 0; i--) sa[--wd[x[i]]] = i;
    for(int j = 1, p = 0; p < n; j *= 2, m = p)
    {
        p = 0;
        for(int i = n - j; i < n; i++) y[p++] = i;
        for(int i = 0; i < n; i++) if(sa[i] >= j) y[p++] = sa[i] - j;
        for(int i = 0; i < n; i++) wc[i] = x[y[i]];
        for(int i = 0; i < m; i++) wd[i] = 0;
        for(int i = 0; i < n; i++) wd[wc[i]]++;
        for(int i = 1; i < m; i++) wd[i] += wd[i - 1];
        for(int i = n - 1; i >= 0; i--) sa[--wd[wc[i]]] = y[i];
        swap(x, y), p = 1, x[sa[0]] = 0;
        for(int i = 1; i < n; i++)
            x[sa[i]] = cmp(y, sa[i - 1], sa[i], j) ? p - 1 : p++;
    }
    return ;
}

int rank[MAXN], height[MAXN];

void calheight(int n)
{
    for(int i = 1; i <= n; i++) rank[sa[i]] = i;
    for(int i = 0, k = 0, j; i < n; i++)
    {
        if(k) k--;
        j = sa[rank[i] - 1];
        while(r[i + k] == r[j + k]) k++;
        height[rank[i]] = k;
    }
}

bool flag;
int n, k, h;
int id[MAXN], rk[MAXN];

inline bool cmp(int a, int b)
{
    return rk[a] >= rk[b];
}

bool cmp(int a[], int b[])
{
    for(int i = 0; i < h; i++)
    {
        if(a[i] > b[i]) return true;
        if(a[i] < b[i]) return false;
    }
    return true;
}

bool ok1(int p)
{
    for(int i = h; i <= n - h; i += h)
        if(!cmp(p, i + p)) return false;
    return true;
}

bool ok2(int p)
{
    int re = n % k - 1;
    for(int i = h; re && (i <= n - h); i += h)
    {
        if(cmp(p, i + p)) re--;
        else i--;
    }
    if(re) return false;
    return true;
}

bool ok(int p)
{
    if(flag) return ok2(p);
    else return ok1(p);
}

char str[MAXN];

int main()
{
//    freopen("B.in", "r", stdin);

    while(~scanf("%d%d%s", &n, &k, str))
    {
        for(int i = 0; i < n; i++) r[i] = str[i] - '0';
        for(int i = 0; i < n; i++) r[i + n] = r[i];

        h = n / k + (n % k ? 1 : 0);
        flag = n % k ? true : false;
        int len = n + h;
        int tmp = r[len];
        r[len] = 0;

        len++;
        da(r, sa, len, 12);
        calheight(len - 1);
        len--;
        r[len] = tmp;

        int cnt = 0;
        for(int i = 1; i <= len; i++) if(sa[i] <= len - h && height[i] < h) id[cnt++] = sa[i];

        len = 2 * n;
        r[len] = 0;

        len++;
        da(r, sa, len, 12);
        calheight(len - 1);
        len--;

        int x = 0;
        for(int i = 1; i <= len; i++)
        {
            if(sa[i] <= len - h)
            {
                if(height[i] >= h) rk[sa[i]] = x;
                else rk[sa[i]] = ++x;
            }
            else rk[sa[i]] = INF;
        }

        int ans = id[cnt - 1];
        for(int i = 0; i < cnt; i++)
        {
            //for(int j = 0; j < h; j++) printf("%d", r[j + id[i]]);  cout << ' ' << rk[id[i]]; puts("");
            if(ok(id[i]))
            {
                ans = id[i];
                break;
            }
        }

        for(int j = 0; j < h; j++) printf("%d", r[j + ans]); puts("");
    }
    return 0;
}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值