团队题库链接:https://www.luogu.org/problemnew/show/T24367
圈圈(cyclic)
【题目描述】
shy 有一个队列 a[1], a[2],…,a[n]。现在我们不停地把头上的元素放到尾巴上。在这过程中我们会得到 n 个不同的队列,每个队列都是 a[k],a[k+1],…,a[n],a[1],…,a[k-1] 的形式。在这些队列中,我们可以找到字典序最小的。
shy 无聊的时候会给队列的每个元素加一玩。但是为了使得游戏不这么无聊,shy 加一以后会给每个元素模 m,这样子字典序最小的序列就会变了,生活就变得有趣。
很显然这样子加 m 次以后,序列会变成原来的样子。所以现在 shy 想知道,在他没有加一前,加一时,加二时,….,加 m-1 时字典序最小的序列的第 k(和上面的 k 没有关系)个元素分别是几。
【输入】
第一行三个整数 n,m,k 表示序列长度,取模的数和要求的序列的第几个元素。
接下来一行 n 个整数表示初始序列。
【输出】
m 个整数表示答案。
【输入样例】
5 6 3
1 2 1
2 3
【输出样例】
1
2
3
5
5
0
【提示】
【数据规模】
对于 30%的数据,1≤n,m≤100;
对于 100%的数据,1≤n,m≤50000, 1≤k≤n, 0≤a[i]
题解
暴力WA了,实际得分0。。。
翻了翻网上的题解说要用lcp,以为要用后缀自动机,吓得弃疗。
然而事实上只需要哈希+二分即可,不知道lcp这个东西也能A。。。orz,经大佬讲解以后豁然开朗然而事实并没有。
首先,当没有数变成零时,字典序最小的序列是不会变的。由于每个数都比m小,所以每个数有且仅有一次会变成零,当且仅当有数变零时,字典序最小的序列会改变,所以我们只需记录在加到几时,有哪些位置的数字变成了零,在那一层我们只需要以那些节点为起点,二分长度比较两个序列找出字典序最小的串就可以求出每层的答案。
为了快速提取出两个串进行比较,我们需要字符串哈希求出前缀和,提取时两个前缀和相减即可。
以基数x为例,abcde中的e的哈希值前缀和 ax4+bx3+cx2+dx+e a x 4 + b x 3 + c x 2 + d x + e ,b的哈希值前缀和为 ax+b a x + b ,当我们想要提取出字符串cde时,只需用b点前缀和,乘以基数的所求字符串长度次方,就变成了 ax4+b3 a x 4 + b 3 ,再相减我们就成功提取出了字符串哈希值就可以进行比较了。
博主依然用的自然溢出(懒得去摸),再加一个双哈希(RP好的同学可以尝试单哈希)。
代码
#include<bits/stdc++.h>
#define ull unsigned long long
using namespace std;
const int M=100005;
int a[M],ans[M],n,m,k,r;
ull base[2],key[2][M],h[2][M];
vector<int>g[M];
char c;
int read()
{
r=0;
c=getchar();
while(!isdigit(c))c=getchar();
while(isdigit(c))r=(r<<1)+(r<<3)+c-'0',c=getchar();
return r;
}
void out(int x)
{
if(x>9)out(x/10);
putchar(x%10+'0');
}
void getkey()
{
base[0]=19260817;
base[1]=19491001;
key[0][0]=key[1][0]=1;
for(int i=1;i<M;++i)
for(int p=0;p<2;++p)
key[p][i]=key[p][i-1]*base[p];
}
void in()
{
// scanf("%d%d%d",&n,&m,&k);
n=read();m=read();k=read();
k--;
for(int i=1;i<=n;++i)
{
// scanf("%d",&a[i]);
a[i]=read();
a[i+n]=a[i];
g[(m-a[i])%m].push_back(i);
}
}
bool check(int x,int y,int len)
{
if(!len)return 1;
if(x>y)swap(x,y);
ull ha,hb,ah,bh;
for(int p=0;p<2;++p)
{
ha=hb=0;
if(x>0)ha=h[p][x-1]*key[p][len];
if(y>0)hb=h[p][y-1]*key[p][len];
ah=h[p][x+len-1]-ha;
bh=h[p][y+len-1]-hb;
if(ah!=bh)return 0;
}
return 1;
}
bool cmp(int x,int y,int t)
{
int le=0,ri=n,len=-1,mid;
while(le<=ri)
{
mid=(le+ri)>>1;
if(check(x,y,mid)) len=mid,le=mid+1;
else ri=mid-1;
}
if(len==n)return 0;
if((a[x+len]+t)%m<(a[y+len]+t)%m)return 1;
return 0;
}
void ac()
{
int hh=n<<1;
h[0][0]=h[1][0]=1;
for(int i=1;i<=hh;++i)
for(int p=0;p<2;++p)
h[p][i]=h[p][i-1]*base[p]+a[i];
int p=1;
for(int i=2;i<=n;++i)
if(cmp(i,p,0))p=i;
ans[0]=a[p+k];
for(int i=1;i<m;++i)
{
if(!g[i].size())ans[i]=ans[i-1]+1;
else
{
p=g[i][0];
for(int j=g[i].size()-1;j>=1;--j)
if(cmp(g[i][j],p,i))p=g[i][j];
ans[i]=(a[p+k]+i)%m;
}
}
for(int i=0;i<m;++i)
// printf("%d\n",ans[i]);
out(ans[i]),putchar(10);
}
int main()
{
getkey();in();ac();
return 0;
}