【Codeforces 1213F】Unstable String Sort(线段合并)

题目链接

开始我想的是对于 ( i , j ) (i,j) (i,j),如果在P中 i i i j j j前,而在Q中 i i i j j j后,则 ( i , j ) (i,j) (i,j)必须同字母,连条边,最后看看有没有 k k k个连通块。然而这是错的,因为如果 P [ a ] P[a] P[a] P [ b ] P[b] P[b]位置必须同字母,那么 P [ a . . b ] P[a..b] P[a..b]的全部位置都得同字母。

想到这里正解就出来了,对每个 P [ i ] P[i] P[i]找到最小的 j j j,使得 P [ j ] P[j] P[j]的位置在Q中位于 P [ i ] P[i] P[i]的后方(为了方便,认为 i i i自身也是一个合法的 j j j),那么 P [ j . . i ] P[j..i] P[j..i]就必须同字母。如果两个同字母段相交,那么合并起来的整一段都必须同字母,因此处理完所有字母段再扫一遍,把相交的同字母段合并。最后看看有没有 k k k段,贪心地让不同字母段用不同的字母即可,复杂度 O ( n ) O(n) O(n)

还有一种比较巧妙的方法, P [ i ] P[i] P[i] P [ i − 1 ] P[i-1] P[i1]连边, Q [ i ] Q[i] Q[i] Q [ i − 1 ] Q[i-1] Q[i1]连边,边 ( x , y ) (x,y) (x,y)就表示 x x x位置上的字母必须不小于 y y y位置,那么一个强连通分量的所有位置必须同字母,一遍tarjan即可。

 

#include<stdio.h>
#include<algorithm>
#include<iostream>
#include<queue>
#include<cmath>
#include<string.h>
#include<set>
#define LL long long
using namespace std;
LL read( )
{
  LL sum=0;char c=getchar( );bool f=0;
  while(c<'0' || c>'9') {if(c=='-') f=1;c=getchar( );}
  while(c>='0' && c<='9') {sum=sum*10+c-'0';c=getchar( );}
  if(f) return -sum;
  return sum;
}
const int N=200005;
int n,m,P[N],Q[N],pos[N],L[N],bel[N];
char ans[N];
int main( )
{
  int i,j,k;
  n=read( );m=read( );
  for(i=1;i<=n;i++) P[i]=read( ),pos[P[i]]=i;
  for(i=1;i<=n;i++) Q[i]=read( );
  for(k=n,i=n;i>=1;i--) j=pos[Q[i]],k=min(k,j),L[j]=k;
  for(j=1,k=n,i=n;i>=1;i--)
    {
      k=min(k,L[i]);bel[i]=j;
      if(k==i) j++;
    }
  if(bel[1]<m) {puts("NO");return 0;}
  puts("YES");
  for(j=-1,i=1;i<=n;i++)
    {
      if(bel[i]!=bel[i-1]) j++;
      ans[P[i]]='a'+min(25,j);
    }
  for(i=1;i<=n;i++) printf("%c",ans[i]);
  return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值