Gym - 101889M Marblecoin (后缀数组妙用)

版权声明:Why is everything so heavy? https://blog.csdn.net/lzc504603913/article/details/83042360

题意:给你N个栈,每天从任意一个栈中取出一个数,数在手上每保留一天,价值*365。所有数字拿完后,求手上的数子的最小价值。

解题思路:看到题目,初始数字不大于365,所以很容易想到贪心,每次必然是取这N个栈中,栈顶最小的那个数。

然后就会想到用优先队列维护每个栈的栈顶。但是这样有一个问题,栈顶相同怎么办?那就看第二个数谁大,还一样就看第三个数。所以就要想着怎么给这N个栈动态的排序。所以就联想到了后缀数组。

我们把N个栈拼在一起,按照字符串那样去看待,就会发现,所求得的后缀数组就是我们要求的功能,动态的排序!

我们主要是利用的rank数组,之前的优先队列是维护栈顶的数字,现在优先队列维护的是,栈顶的那个数字所在的栈的排位。每次取排位最小的那个,肯定是对的。

然后用一个没用的数字表示栈空即可,具体看代码!

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int MAXN = 500005;
const ll MOD=1000000007ll;
int wa[MAXN], wb[MAXN], wv[MAXN], JS[MAXN]; //计算SA用的辅助数组
int rk[MAXN], height[MAXN], SA[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 i, j, p, *x = wa, *y = wb, *t;
    for (i = 0; i < m; i++)
        JS[i] = 0;
    for (i = 0; i < n; i++)
        JS[x[i] = r[i]]++;
    for (i = 1; i < m; i++)
        JS[i] += JS[i - 1];
    for (i = n - 1; i >= 0; i--)
        SA[--JS[x[i]]] = i;
    for (j = 1, p = 1; p < n; j *= 2, m = p)
    {
        for (p = 0, i = n - j; i < n; i++)
            y[p++] = i;
        for (i = 0; i < n; i++)
            if (SA[i] >= j)
                y[p++] = SA[i] - j;
        for (i = 0; i < n; i++)
            wv[i] = x[y[i]];
        for (i = 0; i < m; i++)
            JS[i] = 0;
        for (i = 0; i < n; i++)
            JS[wv[i]]++;
        for (i = 1; i < m; i++)
            JS[i] += JS[i - 1];
        for (i = n - 1; i >= 0; i--)
            SA[--JS[wv[i]]] = y[i];
        for (t = x, x = y, y = t, p = 1, x[SA[0]] = 0, i = 1; i < n; i++)
            x[SA[i]] = cmp(y, SA[i - 1], SA[i], j) ? p - 1 : p++;
    }
    for (i = 0; i < n; i++)
            rk[SA[i]] = i;
    return;
}
/*******************/

int head[MAXN];
int s[MAXN];
struct node{
    int v;
    bool operator <(const node &a)const{
        return rk[head[v]]>rk[head[a.v]];
    }
};

priority_queue<node> que;
int main(){

    int N;
    int now=0;
    scanf("%d",&N);
    for(int i=1;i<=N;i++){
        head[i]=now;//栈顶是字符串的哪个位置
        int k,x;
        scanf("%d",&k);
        for(int i=1;i<=k;i++){
            scanf("%d",&x);
            s[now++]=x;
        }
        s[now++]=301;//代表这个栈结束
    }
    s[now++]=0;
    DA(s,SA,now,305);

    for(int i=1;i<=N;i++)//先把所有“栈”插入优先队列
    {
        node v1;
        v1.v=i;
        que.push(v1);
    }

    ll ans=0;
    while(!que.empty()){
        int tp=que.top().v;
        que.pop();
        ans=(ans*365ll+s[head[tp]])%MOD;//hash思想,计算答案
        head[tp]++;//下一个
        if(s[head[tp]]!=301)//没到栈底,重新入队
        {
            node v1;
            v1.v=tp;
            que.push(v1);
        }
    }
    printf("%lld\n",ans*365ll%MOD);

    return 0;
}










猜你喜欢

转载自blog.csdn.net/lzc504603913/article/details/83042360