队列变换「USACO 2007」

【题目描述】
FJ打算带他的\(N(1 \leq N \leq 30,000)\)头奶牛去参加一年一度的“全美农场主大奖赛”。在这场比赛中,每个参赛者都必须让他的奶牛排成一列,然后领她们从裁判席前依次走过。 今年,竞赛委员会在接受队伍报名时,采用了一种新的登记规则:他们把所有队伍中奶牛名字的首字母取出,按它们对应奶牛在队伍中的次序排成一列(比如说,如果FJ带去的奶牛依次为Bessie、Sylvia、Dora,登记人员就把这支队伍登记为BSD)。登记结束后,组委会将所有队伍的登记名称按字典序升序排列,就得到了他们的出场顺序。 FJ最近有一大堆事情,因此他不打算在这个比赛上浪费过多的时间,也就是说,他想尽可能早地出场。于是,他打算把奶牛们预先设计好的队型重新调整一下。 FJ的调整方法是这样的:每次,他在原来队列的首端或是尾端牵出一头奶牛,把她安排到新队列的尾部,然后对剩余的奶牛队列重复以上的操作,直到所有奶牛都被插到了新的队列里。这样得到的队列,就是FJ拉去登记的最终的奶牛队列。 接下来的事情就交给你了:对于给定的奶牛们的初始位置,计算出按照FJ的调整规则所可能得到的字典序最小的队列。

【输入格式】
第1行: 一个整数:N
第2..N+1行: 第i+1行仅有1个‘A’..‘Z’中的字母,表示队列中从前往后数第i 头奶牛名字的首字母

【输出格式】
输出FJ所能得到的字典序最小的队列。每行(除了最后一行)输 出恰好80个‘A’..‘Z’中的字母,表示新队列中每头奶牛姓名的首字母。

题解

第一篇写后缀数组的题解 先放下模板代码

inline bool check(int *num, int a, int b, int l) { return num[a] == num[b] && num[a+l] == num[b+l]; }

void suffix() {
    int i, j, p;
    for (i = 1; i <= m; i++) sum[i] = 0;
    for (i = 1; i <= n; i++) sum[rnk[i]=r[i]]++;
    for (i = 2; i <= m; i++) sum[i] += sum[i-1];
    for (i = n; i >= 1; i--) sa[sum[rnk[i]]--] = i;
    for (j = 1, p = 0; p <= n; j <<= 1, m = p) {
        p = 0; for (i = n - j + 1; i <= n; i++) sa2[++p] = i;
        for (i = 1; i <= n; i++) if (sa[i] > j) sa2[++p] = sa[i] - j;
        for (i = 1; i <= n; i++) key1[i] = rnk[sa2[i]];
        for (i = 1; i <= m; i++) sum[i] = 0;
        for (i = 1; i <= n; i++) sum[key1[i]]++;
        for (i = 2; i <= m; i++) sum[i] += sum[i-1];
        for (i = n; i >= 1; i--) sa[sum[key1[i]]--] = sa2[i];
        for (swap(rnk, sa2), p = 2, rnk[sa[1]] = 1, i = 2; i <= n; i++) {
            rnk[sa[i]] = check(sa2, sa[i-1], sa[i], j) ? p - 1 : p++;
        }
    }
}

sa[i]表示字典序为第i名的后缀的起始位置 rnk[i]表示起始位置为i的后缀的排名
对这道题有什么用呢

先考虑一下暴力做法
双指针l, r记录当前已经把a[1~l-1]及a[r+1~n]的奶牛扔到新队列里,现在原队列首尾分别是牛a[l]和a[r]

  1. 若a[l]<a[r] 那么为使新队列字典序最小 显然要将a[l]先放到新队列里 反之亦然
  2. 若a[l]==a[r] 此时并不能直接判断要先把a[l]放入还是要先把a[r]放入
    举个例子 样例: ACDBCB 将a[1], a[6]先后放入新队列后发现此时a[2]==a[5] 显然是要先将a[5]放入 以便下一个放a[4] 比较优
    可以看出其实就是比较一下 字符串{a[2], a[3]} 和 {a[5], a[4]} 哪个字典序较小
    直接比较是O(n)的 考虑如何用后缀数组优化

将a[1]~a[n]反转后接在后面 如样例: ACDBCB -> ACDBCB|BCBDCA
然后此时比较 字符串{a[2], a[3]} 和 {a[5], a[4]} 的大小就等同于比较 后缀[2~2*n]和后缀[8~2*n]的大小

p.s. 这个技巧也经常用于处理回文串有关的问题
后缀大小排名是可以O(nlogn)预处理的 查询时就可以把r对称到另一边去 实现O(1)查询
总时间复杂度O(nlogn)

代码

#include <bits/stdc++.h>
#define mx 1000005
using namespace std;

char one_time_AC_accepted[5];
char s[mx], ans[mx];
int n, m, len;
int sa[mx], sa2[mx], rnk[mx], key[mx], sum[mx], height[mx];

inline bool check(int *num, int a, int b, int l) { return num[a] == num[b] && num[a+l] == num[b+l]; }

inline void suffix() {
    int i, j, p; int *_rnk = rnk, *_sa2 = sa2, *tmp;
    for (i = 1; i <= m; i++) sum[i] = 0;
    for (i = 1; i <= 2*n; i++) sum[_rnk[i]=s[i]]++;
    for (i = 2; i <= m; i++) sum[i] += sum[i-1];
    for (i = 2*n; i >= 1; i--) sa[sum[_rnk[i]]--] = i;
    for (j = 1; p <= 2*n; j <<= 1, m = p) {
        p = 0;
        for (i = n*2 - j + 1; i <= 2*n; i++) _sa2[++p] = i;
        for (i = 1; i <= 2*n; i++) if (sa[i] > j) _sa2[++p] = sa[i] - j;
        for (i = 1; i <= 2*n; i++) key[i] = _rnk[_sa2[i]];
        for (i = 1; i <= m; i++) sum[i] = 0;
        for (i = 1; i <= n*2; i++) sum[key[i]]++;
        for (i = 2; i <= m; i++) sum[i] += sum[i-1];
        for (i = 2*n; i >= 1; i--) sa[sum[key[i]]--] = _sa2[i];
        for (tmp = _rnk, _rnk = _sa2, _sa2 = tmp, p = 2, _rnk[sa[1]] = 1, i = 2; i <= 2*n; i++) {
            _rnk[sa[i]] = check(_sa2, sa[i-1], sa[i], j) ? p - 1 : p++;
        }
    }
} 

inline void print() {
    for (int i = 1; i <= len; i++) {
        putchar(ans[i]);
        if (i % 80 == 0) puts("");
    }
}

inline bool check(int l0, int r0) {
    return rnk[(n<<1)-r0+1] > rnk[l0];
}

int main() {
    scanf("%d", &n); m = 128;
    for (int i = 1; i <= n; i++) {
        scanf("%s", one_time_AC_accepted);
        s[i] = one_time_AC_accepted[0];
    }
    for (int i = 1; i <= n; i++) {
        s[n * 2 - i + 1] = s[i];
    }
    suffix();
    int l = 1, r = n;
    for (int i = 1; i <= n; i++) {
        if (s[l] < s[r]) {
            ans[++len] = s[l]; l++;
        } else if (s[l] > s[r]) {
            ans[++len] = s[r]; r--;
        } else {
            if (check(l, r)) {
                ans[++len] = s[l]; l++;
            } else {
                ans[++len] = s[r]; r--;
            }
        }
    }
    print();
    return 0;
} 

```cpp

原文地址:https://www.cnblogs.com/ak-dream/p/AK_DREAM27.html

时间: 2024-08-30 14:22:08

队列变换「USACO 2007」的相关文章

护城河的挖掘「USACO 2006」

题意 凸包模板,给定平面上点集,求包含所有点的凸包周长最小值. 思路 使用\(Graham\)扫描法解决. 考虑将最左下的点设为原点(事实上任意点均可作为原点),然后其余各点根据斜率排序. 对于每一个节点,我们考虑加入它是否会与已有的边点构成内凹,如果会,那么放弃已有边点. 显然我们可以通过维护一个单调栈完成该过程. 代码 #include <bits/stdc++.h> using namespace std; namespace StandardIO { template<typen

产奶模式「USACO 2006」

[题目描述] 农夫John发现他的奶牛产奶的质量一直在变动.经过细致的调查,他发现:虽然他不能预见明天产奶的质量,但连续的若干天的质量有很多重叠.我们称之为一个"模式". John的牛奶按质量可以被赋予一个\(0\)到\(1000000\)之间的数.并且John记录了\(N(1\le N\le 20000)\)天的牛奶质量值.他想知道最长的出现了至少\(K(2\le K\le N)\)次的模式的长度.比如1 2 3 2 3 2 3 1 中 2 3 2 3出现了两次.当\(K=2\)时,

3D高科技投影 麦可「复活登台」幕后

美国告示牌音乐颁奖典礼,日前在赌城盛大举行,主办单位利用高科技投影技术,让麦可杰克森「复活」登台表演,3D全像投影,加上影片与真人舞群无缝接轨,高科技让过世的大明星彷佛活了过来. 流行乐天王麦可杰克森死而复生,过世将近5年的他,又现身在今年美国告示牌音乐颁奖典礼上,金光闪闪现身舞台中央,麦可杰克森回来了,再现招牌的动感舞步,流露巨星风采,主办单位利用3D全像摄影技术,秘密制作了半年多,把他带回到世人眼前. 特效专家:「观众在告示牌典礼上看到的是,麦可的头部数字影像,连接到一名演员身上,我们实时捕

「ZigBee模块」协议栈-Z-Stack协议栈基础和数据传输实验

花了好久写的...感觉还不错的呢...如果看,请细看...Mua~ Z-Stack协议栈基础和数据传输实验 一.实验目的 终端节点将数据无线发送到协调器,协调器通过串口将数据发送到PC端,并在屏幕上显示出来.串口优化把有线串口传输改为无线蓝牙传输. 二.实验平台 硬件:2个zigbee节点,1个编译器,1根方口转USB数据线,一个蓝牙模块 软件:实验基于SampleApp工程进行. 三.实验步骤 串口初始化代码 发送部分代码 接收部分代码 四.协议栈基础 做实验之前先了解一点关于协议栈的基础知识

BZOJ 1692: [Usaco2007 Dec]队列变换( 贪心 )

数据 n <= 30000 , 然后 O( n² ) 的贪心也过了..... USACO 数据是有多弱啊 = = ( ps : BZOJ 1640 和此题一模一样 , 双倍经验 ) -------------------------------------------------------------------------------------- #include<cstdio> #include<cstring> #include<algorithm> #

「不会」等价类计数

完全忘了TnT 然而这种类型的题目好像没考过.. 先复习一下万能的burnside引理, 啊不先复习一下定义(有些是本蒻自己yy的可能并不准确) 一个物体:被染色的对象 一个元素:一种染色方案 一个置换\(g\):一种让物体交换位置的变换方法 一个置换群\(G\):里面的置换满足封闭性结合律单位元逆元 一个循环(对于一个置换i来说的):不停地用一个置换作用于所有物体,物体呈现循环运动的轨迹(数目记为\(h_i\)) 一个不动点(对于一个置换i来说的):被该置换作用后,不发生改变的一个元素(数目记

怎样将「插件化」接入到项目之中?

本期移动开发精英社群讨论的主题是「插件化」,上网查了一下,发现一篇 CSDN 博主写的文章<Android 使用动态载入框架DL进行插件化开发>.此处引用原作者的话: 随着应用的不断迭代,应用的体积不断增大,项目越来越臃肿,冗余添加.项目新功能的加入,无法确定与用户匹配性,发生严重异常往往牵一发而动全身,仅仅能紧急公布补丁版本号,强制用户进行更新.结果频繁的更新.反而easy减少用户使用黏性,或者是公司业务的不断发展,同系的应用越来越多,传统方式须要通过用户量最大的主项目进行引导下载并安装.

bzoj1640[Usaco2007 Nov]Best Cow Line 队列变换*&amp;&amp;bzoj1692[Usaco2007 Dec]队列变换*

bzoj1640[Usaco2007 Nov]Best Cow Line 队列变换 bzoj1692[Usaco2007 Dec]队列变换 题意: 有一个奶牛队列.每次可以在原来队列的首端或是尾端牵出一头奶牛,把她安排到新队列的尾部,然后对剩余的奶牛队列重复以上的操作,直到所有奶牛都被插到了新的队列里.这样得到的队列,就是FJ拉去登记的最终的奶牛队列. 求对于给定的奶牛们的初始位置,计算出可能得到的字典序最小的队列.队列大小≤30000. 题解: 有一个结论:如果当前队列中的队首元素不等于队尾元

「C语言」常量和变量的表示及应用

先发布,还在修改完善中.. 在程序运行中,其值不能改变的量成为常量.在基本数据类型中,常量可分为整型常量.实型常量.符号常量和字符型常量(包括字符常量和字符串常量),现分别介绍如下: 整型常量 即整常数,由一个或多个数字组成,可以带正负号 C语言中整型常量可用十进制.八进制和十六进制3种形式表示 十进制整数:由0~9数字组成,不能以0开始,没有前缀 八进制整数:以0为前缀,其后由0~7的数字组成,没有小数部分 十六进制整数:以0x或0X开头,其后由0~9的数字和a~f(或A~F字母组成) 另外长