这道题赛时是想出来了,但是线段树出锅了,只得了20分。
首先根据题目我们可以得出,一个序列要是为T的子串,就必须满足以下三个条件。
1.该序列单调不降。如 ba肯定是不合法的。
2.该序列除去开头和结尾的字母,其他字母的数量必须为整个序列全部的该字母数量。
如 abbcba 选择 abbc a和c不用管,序列中只出现了一次b,而整个序列有2个b,故所选序列是不合法的。
3.在满足单调不降的条件下,相邻两个字母的之间不能出现还有字母按字典序排列位于他俩之间。
如 acb 选取ac这个序列,很明显是满足前两个条件,但它是不合法的。
好,明白这些,我们再想如何用线段树来维护,关于单调不降,我们可以想到之前所做的山海经,我们可以维护一个左边界和右边界,再维护一个vis标记,表示该区间是否合法。
只有在满足左区间和右区间同时合法并且左区间的有边界要小于右区间的左边界时才合法(这算是最难维护的一点)。
剩下两个都比较好说,直接遍历一遍26个字母即可。
点击查看代码
#include<bits/stdc++.h>
using namespace std;const int N=3e5+100;
int n,q;
char s[N];
int cnt[N];
#define lson (rt<<1)
#define rson (rt<<1|1)
struct lmy
{int l,r;int vis;int ls,rs;int cnt[30];
}tr[N<<2];void pushup(int rt)
{for(int i=0;i<=26;i++) tr[rt].cnt[i]=tr[lson].cnt[i]+tr[rson].cnt[i];if(tr[lson].rs<=tr[rson].ls&&tr[lson].vis&&tr[rson].vis) tr[rt].vis=1;else tr[rt].vis=0;tr[rt].ls=tr[lson].ls; tr[rt].rs=tr[rson].rs;
}void build(int rt,int l,int r)
{tr[rt].l=l;tr[rt].r=r;if(l==r){tr[rt].cnt[s[l]-'a']++;tr[rt].ls=tr[rt].rs=s[l]-'a';tr[rt].vis=1;return ;}int mid=(l+r)>>1;build(lson,l,mid); build(rson,mid+1,r);pushup(rt);
}void update(int rt,int x,char ch)
{if(tr[rt].l==tr[rt].r){tr[rt].cnt[s[x]-'a']--;s[x]=ch;tr[rt].cnt[s[x]-'a']++;tr[rt].ls=tr[rt].rs=s[x]-'a';return ;}int mid=(tr[rt].l+tr[rt].r)>>1;if(x<=mid) update(lson,x,ch);else update(rson,x,ch);pushup(rt);
}
lmy query(int rt,int l,int r)
{if(l<=tr[rt].l&&tr[rt].r<=r){return tr[rt];}int mid=(tr[rt].l+tr[rt].r)>>1;lmy a,b,c;if(l<=mid&&r<=mid) return a=query(lson,l,r);if(r>mid&&l>mid) return b=query(rson,l,r);if(l<=mid&&r>mid){a=query(lson,l,r);b=query(rson,l,r); if(a.rs<=b.ls&&a.vis&&b.vis) c.vis=1;else c.vis=0;for(int i=0;i<=26;i++) {c.cnt[i]=a.cnt[i]+b.cnt[i];}c.rs=b.rs;c.ls=a.ls;return c; }}int main()
{
// freopen("in.in","r",stdin);
// freopen("0.out","w",stdout);scanf("%d",&n);scanf("%s",s+1);for(int i=1;i<=n;i++){cnt[s[i]-'a']++;}build(1,1,n);scanf("%d",&q);for(int i=1;i<=q;i++){int op;scanf("%d",&op);if(op==1){int x;char ch;scanf("%d %c",&x,&ch);cnt[s[x]-'a']--;update(1,x,ch);cnt[s[x]-'a']++;}else{int l,r;scanf("%d%d",&l,&r);lmy a=query(1,l,r);int flag=a.vis;int i=0;int last=26;while(i<=26){if(a.cnt[i]){for(int j=last+1;j<i;j++){if(cnt[j]!=0) flag=0;}last=i;if(i==s[l]-'a'||i==s[r]-'a'){i++;continue;} else if(cnt[i]!=a.cnt[i]) {flag=0;} }i++;}if(flag) printf("Yes\n");else printf("No\n");}}
}