计科陈冠希


私信TA

用户名:uq_80401439734

访问量:950

签 名:

自我感觉很帅

等  级
排  名 151
经  验 7257
参赛次数 10
文章发表 5
年  龄 99
在职情况 学生
学  校 西北师范大学
专  业 计算机科学与技术

  自我简介:

PUA被干爆了

解题思路:
1、首先序列的变化是由于队列中出现大于m的数字取模而改变序列的字典序最小而造成的,所以在序列中没有出现取模的时候序列不会变

2、确定最小字典序是通过找到最长公共序列来确定:比如101012的最小字典序为什么是第一个1而不是第三个1,此时我们寻找的最长公共子序列应该是10101,若第三个作为队首,此时得到的序列是121010,当然不是字典序最小。

3、通过二分枚举长度来找到最长的序列的长度

4、通过字符串哈希来存储目标序列,通过前缀和来寻找[l.r]之间的序列情况(类似于k进制数)
注意事项:

代码的初始化:1、最好是先去确定那些数字会因为+i而变化取模,这样会少很多次遍历寻找取模数

2、不用实现队列,我们只需要把数列长为n的复制一份(从n+1处)这样其实和队列的效果一样的。


参考代码

#include<iostream>
#include<vector>
using namespace std;
const int N=50010*2;
typedef unsigned long long ll;
long long base=131;
ll arr[N],ha[N],p[N];
int n,m,k,ans;
vector<int> v[N];
int find(int x,int y,int add)//当队列变化时,寻找x与y的最长公共子序列,返回下一次调整好的队首
{
	int l=0,r=n;//枚举区间为0~n,长度取中间
	while(l<r)
	{
		int mid = l+r+1>>1;//mid是枚举长度
		if(ha[x+mid-1]-ha[x-1]*p[mid]==ha[y+mid-1]-ha[y-1]*p[mid]) l=mid;//若公共序列相等,则说明枚举区间太端,需要拉长区间
		else r=mid-1;//否则压缩区间
	}
	if(l==n) return x;//区间长度是最大的n,此时说明以x与y开始的最长公共序列的长度是一样的,即返回x和返回y效果一样
	if((arr[x+l]+add)%m<=(arr[y+l]+add)%m) return x;//若长度没有达到n,说明x和y长度不一样,则需要比较下一位谁最小,小的则为下次队列的队首
	else return y;
}
int main()
{
	ios::sync_with_stdio(false);
	cin.tie(nullptr);
	cout.tie(nullptr);
	cin>>n>>m>>k;
	for(int i=1;i<=n;i++)
	{
		cin>>arr[i];
		arr[i+n]=arr[i];
		v[(m-arr[i])%m].push_back(i);
	}
	p[0]=1;
	for(int i=1;i<=2*n;i++) p[i]=p[i-1]*base;//进制数组,类似10的0次方为1,一次方为10,二次方为100
	for(int i=1;i<=2*n;i++) ha[i]=ha[i-1]*base+arr[i];//字符串哈希 将目标数组通过哈希数组来存 
	ans=1;
	for(int i=2;i<=n;i++)
	{
		ans= find(ans,i,0);//寻找第一次的队首
	}
	cout<<arr[ans+k-1]%m<<'\n';
	for(int i=1;i<m;i++)
	{
		if(v[i].size())
		{
			ans=v[i][0];
			for(int j=1;j<v[i].size();j++) ans=find(ans,v[i][j],i);
		}
		cout<<(arr[ans+k-1]+i)%m<<'\n';
	}
	return 0;
}

最后欢迎大佬指正

 

0.0分

12 人评分

  评论区

  • «
  • »