题解:P3279 [SCOI2013] 密码
我既不会 manacher,也不会并查集,所以我没用任何算法过掉了这道题。
考虑题目给的信息代表了什么,是不是一堆点对一定相同,一对点一定不同。
我们可以从前往后填,首先第一个位置一定是 a,这点很明显。然后对于每一个最长回文串,都直接从前面直接复制到后面,然后用一个 vis 数组记录一个点哪个字符不能填,然后遇到没被复制过的点,就从 a 开始,找到这个点能填的最小字符。这样我们就成功得到了一个 \(O(n^2)\) 的做法。
这是 \(O(n^2)\) 的暴力代码
#include <bits/stdc++.h>
#define fo(i,a,b) for(int i=a;i<=b;i++)
#define fd(i,a,b) for(int i=a;i>=b;i--)
using namespace std;
const int N=100020;
int n,a[N],b[N],now='a';
char s[N];
bool vis[N][26];
signed main()
{
cin>>n;
fo(i,1,n)scanf("%d",&a[i]),a[i]/=2,s[i]='a';
fo(i,2,n)scanf("%d",&b[i]),b[i]/=2;
s[1]='a';
fo(i,2,n)
{
fo(j,1,b[i])s[i+j-1]=s[i-j];
if(i-b[i]-1)vis[i+b[i]][s[i-b[i]-1]-'a']=1;
fo(j,1,a[i])s[i+j]=s[i-j];
if(i-a[i]-1)vis[i+a[i]+1][s[i-a[i]-1]-'a']=1;
while(vis[i][s[i]-'a'])s[i]++;
}
fo(i,1,n)putchar(s[i]);
return 0;
}
其实这代码能过(数据过水)
怎么优化呢?发现其实很多东西都是重复复制的,因为这题目保证一定有解,那我们复制过的肯定不用再复制,考虑维护一个已经复制过的最后位置 mx,每次就从 mx 开始复制,这样因为每个点最多被复制一次,所以复杂度是 \(O(n)\) 的。
这是正解代码
#include <bits/stdc++.h>
#define fo(i,a,b) for(int i=a;i<=b;i++)
#define fd(i,a,b) for(int i=a;i>=b;i--)
using namespace std;
const int N=100020;
int n,a[N],b[N],now='a',mx;
char s[N];
bool vis[N][26];
signed main()
{
cin>>n;
fo(i,1,n)scanf("%d",&a[i]),a[i]/=2,s[i]='a';
fo(i,2,n)scanf("%d",&b[i]),b[i]/=2;
s[1]='a';mx=1;
fo(i,2,n)
{
fo(j,mx-i+2,b[i])s[i+j-1]=s[i-j];
mx=max(i+b[i]-1,mx);
if(i-b[i]-1)vis[i+b[i]][s[i-b[i]-1]-'a']=1;
fo(j,mx-i+1,a[i])s[i+j]=s[i-j];
mx=max(i+a[i],mx);
if(i-a[i]-1)vis[i+a[i]+1][s[i-a[i]-1]-'a']=1;
while(vis[i][s[i]-'a'])s[i]++;
}
fo(i,1,n)putchar(s[i]);
return 0;
}
加个快读就最优解了。

浙公网安备 33010602011771号