牛客多校3 - Sort the Strings Revision(笛卡尔树+分治)

题目链接:点击查看

题目大意:给出一个长度为 n 的数字串 s[ 0 ],每个位置的赋值初始时为 s[ i ] = i % 10 ( i ∈ [ 0 , n - 1 ] ),现在有一个长度为 n 的排列 p,和一个长度为 n 的数列 d ,相当于 n 次操作,每次操作需要将第 p[ i ] 个位置的数字变为 d[ i ] ,这样一共能得到 n + 1 个数字串,需要给这 n + 1 个数字按照字典序排序

题目分析:显然是不能构造出 n + 1 个串然后排序的,而且数据范围也限制了只能 O( n ) 实现,带个 log 都有可能 T 飞

因为 n 次操作对于每个位置有且仅有一次替换,而且在字典序的排序中,显然在原数列中位置更靠前的替换的影响要大于位置较靠后的替换,这样我们可以分治去做:假设当前分治的排列区间是 [ l , r ],设 pos 为 p[ l ] ~ p[ r ] 中的最小值

  1. p[ pos ] % 10 > d[ pos ],替换后数列会变得更小,s[ 0 ] ~ s[ pos ] 的字典序要大于 s[ pos + 1 ] ~ s[ n ] 的字典序
  2. p[ pos ] % 10 < d[ pos ],替换后数列会变得更大,s[ 0 ] ~ s[ pos ] 的字典序要小于 s[ pos + 1 ] ~ s[ n ] 的字典序
  3. p[ pos ] % 10 == d[ pos ],替换后数列不变

现在的问题是,如何 O( n ) 求出每个区间的最小值呢?可以用笛卡尔树,无脑跑出笛卡尔树后,就可以dfs遍历每个节点所能覆盖的区间了

我写的笛卡尔树一般喜欢加个哨兵节点当做 root,所以在 dfs 的时候需要特判一下,防止死循环

代码:
 

#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
using namespace std;
 
typedef long long LL;
 
typedef unsigned long long ull;
 
const int inf=0x3f3f3f3f;
 
const int N=2e6+100;

const int mod=1000000007;

const int Hash=10000019;

struct Node
{
	int l,r;
}tree[N];

int p[N],d[N],rk[N],root=N-1,num;

stackst;

void build(int n)
{
	for(int i=0;ip[i])
			st.pop();
		tree[i].l=tree[st.top()].r;
		tree[st.top()].r=i;
		st.push(i);
	}
}

void dfs(int k,int l,int r)
{
	if(k==root)
	{
		if(tree[k].l)
			dfs(tree[k].l,l,r);
		else
			dfs(tree[k].r,l,r);
		return;
	}
	if(l>r)
		return;
	if(l==r)
	{
		rk[l]=num++;
		return;
	}
	if(p[k]==inf)
	{
		for(int i=l;i<=r;i++)
			rk[i]=num++;
		return;
	}
	if(p[k]%10>w;
	while(w--)
	{
		int n;
		scanf("%d",&n);
		init(n);
		LL pseed,pa,pb,pmod,dseed,da,db,dmod;
		scanf("%lld%lld%lld%lld%lld%lld%lld%lld",&pseed,&pa,&pb,&pmod,&dseed,&da,&db,&dmod);
		for(int i=0;i

 

你可能感兴趣的:(数据结构,分治)