首页 > 其他分享 >【noip赛前20天冲刺集训 day16】星空遗迹

【noip赛前20天冲刺集训 day16】星空遗迹

时间:2023-11-02 20:14:35浏览次数:38  
标签:right 20 noip res 元素 day16 连续 字符串 left

Description

在石头剪刀布中,一共有三种手势:\(R(Rock), P(Paper), S(Scissors)\),其中 \(R\) 能赢 \(S\),\(S\) 能嬴 \(P\),\(P\) 能赢 \(R\)。

现在,我们定义 \(w(x, y)\) 是 \(x\) 和 \(y\) 中获胜的手势,特别地,如果 \(x\) 和 \(y\) 相同(也就是平局),那么 \(w(x, y)\) 也和 \(x, y\) 均相同。\(x\) 和 \(y\) 均为 \(R, P, S\) 中的某一种。

我们定义一个对长度为 \(n\) 的字符串 \(s\) 的操作 \(f\left(s_1 s_2 \ldots s_n\right)=w\left(s_1, s_2\right) w\left(s_2, s_3\right) \ldots w\left(s_{n-1}, s_n\right)\),也就是说操作结果是一个长度为 \(n-1\) 的字符串,它的第 \(i\) 位恰是 \(w\left(s_i, s_{i+1}\right)\),也就是 \(s_i\) 和 \(s_{i+1}\)中获胜的手势。如 \(f(R R R R)=R R R, f(R S P R)=R S P\)。

我们定义一个长度为 \(n\), 且由 \(R, P, S\) 组成的字符串的“最终胜者”是对这个字符串进行连续 \(n-1\) 次 \(f\) 操作得到的结果,它显然是某个长度为 \(1\) 的字符串,并且是 \(R, P, S\) 中的一种。

现在,给定一个长度为 \(n\) 的字符串,你需要支持 \(q\) 次操作,操作有两种:

\(1\ k\ x\):将这个字符串的第 \(k\) 个字符修改为 \(x\),\(k\) 是 \(1-n\) 之间的整数,\(x \in R, P, S\)。

\(2\ l\ r\):查询这个字符串的第 \(l\) 个字符到第 \(r\) 个字符形成的连续子串的“最终胜者”,\(1 \leq l \leq r \leq n\)。

\(n,q\le 2\times 10^5\)。

Solution

先不考虑修改,看看全局查询怎么做。

对于一个连续的相同段来说,如果其左右两边都是可以赢它的,那么这个连续段就可以直接变成赢它的那个字符。例如,SRRRRS 就可以直接变成 SSSSSS

同时,若以 \(1\) 为开头的一段连续串,其右边是可以赢它的,同样可以将连续串变成赢它的那个字符。同理,以 \(n\) 为结尾的也是一样。

每进行一次上面那个操作,就会少至少一个连续段。可以证明,若干次操作后,整个串会变成一个连续串。

证明:假设最后剩下了至少两个连续串,不妨设有 \(k\) 个。那么一定有第 \(1\) 个能赢第 \(2\) 个,第 \(2\) 个一定能赢第 \(3\) 个。因此类推,第 \(k-1\) 个一定能赢第 \(k\) 个,这样第 \(k\) 个串就能被转换,与原设矛盾。因此最后一定只剩下一个串。

现在考虑对某一个字串进行求解。考虑维护一个栈,记栈底的元素为元素 \(1\),那么使得元素 \(i\) 能赢 \(i-1\)。每次插入元素时,弹出栈顶直到空栈或者栈顶可以赢待插入的元素。那么扫完字串后,栈底元素即为“最终胜者”。时间复杂度 \(\mathcal O(nq)\)。

考虑数字化这个过程。设 \(f_i\) 表示加入当前元素后栈的大小。那么有

\(f_i=\begin{cases}f_{i-1}+1&s_i>s_{i-1}\bigcup i=1\\f_i&s_i=s_{i-1}\\\max(1,f_{i-1}-1)&s_i<s_{i-1}\end{cases}\)

\(x>y\) 表示 \(x\) 能赢 \(y\),\(=\) 表示相等,\(<\) 表示 \(x\) 输给 \(y\)。

那么答案就为最后的 \(f_i=1\) 的 \(s_i\)。

但是,与 \(1\) 取 \(\max\) 是难维护的。你考虑定义一个广泛的栈,这个栈是可以有负下标的。那么就不再需要取 \(\max\),同时答案变成了最小的 \(f_i\)。同时你发现,那些最小的 \(f_i\) 的 \(s_i\) 是相同的。

那么考虑维护 \(f_i\) 的差分 \(d_i\),然后找一个前缀最小值即可。那么现在就是单点修改,区间前缀最小值。用取和的操作转为区间修改加查询区间最小值位置。上线段树即可。复杂度 \(\mathcal O(q\log n)\)。

Code

#include<cstdio>
#define N 200005
#define inf 0x3f3f3f3f
using namespace std;
struct segtree
{
    int val,pos;
}tr[N<<2];
int n,m,a[N],d[N],sum[N],lz[N<<2];
char s[N];
char itc(int x)
{
	if (x==0) return 'R';
	if (x==1) return 'S';
	if (x==2) return 'P';
}
int cti(char x)
{
	if (x=='R') return 0;
	if (x=='S') return 1;
	if (x=='P') return 2;
}
bool pd(int x,int y)
{
    if (x==0&&y==1) return true;
    if (x==1&&y==2) return true;
    if (x==2&&y==0) return true;
    return false;
}
void build(int x,int l,int r)
{
    if (l==r) {tr[x].val=sum[l];tr[x].pos=l;return;}
    int mid=(l+r)>>1;
    build(x<<1,l,mid);build(x<<1|1,mid+1,r);
    if (tr[x<<1].val<=tr[x<<1|1].val) tr[x]=tr[x<<1];
    else tr[x]=tr[x<<1|1];
}
void pushdown(int x)
{
    if (lz[x]!=0)
    {
        tr[x<<1].val+=lz[x];lz[x<<1]+=lz[x];
        tr[x<<1|1].val+=lz[x];lz[x<<1|1]+=lz[x];
        lz[x]=0;
    }
}
void modify(int x,int l,int r,int p,int q,int v)
{
    if (l>=p&&r<=q)
    {
        tr[x].val+=v;
        lz[x]+=v;
        return;
    }
    pushdown(x);
    int mid=(l+r)>>1;
    if (mid>=p) modify(x<<1,l,mid,p,q,v);
    if (mid<q) modify(x<<1|1,mid+1,r,p,q,v);
    if (tr[x<<1].val<=tr[x<<1|1].val) tr[x]=tr[x<<1];
    else tr[x]=tr[x<<1|1];
}
segtree query(int x,int l,int r,int p,int q)
{
    if (l>=p&&r<=q) return tr[x];
    pushdown(x);
    int mid=(l+r)>>1;segtree res;
    res.val=inf;res.pos=0;
    if (mid>=p) res=query(x<<1,l,mid,p,q);
    if (mid<q)
    {
        if (res.val>=inf) res=query(x<<1|1,mid+1,r,p,q);
        else
        {
            segtree ress=query(x<<1|1,mid+1,r,p,q);
            if (ress.val<res.val) res=ress;
        }
    }
    return res;
}
int main()
{
	scanf("%d%d",&n,&m);
	scanf("%s",s+1);
	for (int i=1;i<=n;++i)
		a[i]=cti(s[i]);
    d[1]=1;
    for (int i=2;i<=n;++i)
    {
        if (pd(a[i-1],a[i])) d[i]=1;
        else if (pd(a[i],a[i-1])) d[i]=-1;
        else d[i]=0;
    }
    for (int i=1;i<=n;++i)
        sum[i]=sum[i-1]+d[i];
    build(1,1,n);
	while (m--)
	{
		int ty;
		scanf("%d",&ty);
		if (ty==1)
		{
			int x;scanf("%d",&x);
			char ch=getchar();
			while (ch!='R'&&ch!='S'&&ch!='P') ch=getchar();
            int bef,aft,now=cti(ch);
            if (x!=1)
            {
                bef=d[x];aft=0;
                if (pd(a[x-1],now)) aft=1;
                if (pd(now,a[x-1])) aft=-1;
                d[x]=aft;
                modify(1,1,n,x,n,aft-bef);
            }
            if (x!=n)
            {
                bef=d[x+1];aft=0;
                if (pd(now,a[x+1])) aft=1;
                if (pd(a[x+1],now)) aft=-1;
                d[x+1]=aft;
                modify(1,1,n,x+1,n,aft-bef);
            }
            a[x]=now;
		}
		else
		{
			int x,y;scanf("%d%d",&x,&y);
            printf("%c\n",itc(a[query(1,1,n,x,y).pos]));
		}
	}
	return 0;
}

标签:right,20,noip,res,元素,day16,连续,字符串,left
From: https://www.cnblogs.com/Livingston/p/17806179.html

相关文章