洛谷P2787 语文1(chin1)- 理理思维【暴力ODT珂朵莉树】x【正解线段树】

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/niiick/article/details/83062725

时空限制 1000ms / 128MB

题目描述

考试开始了,可是蒟蒻HansBug脑中还是一片空白。哦不!准确的说是乱七八糟的。现在首要任务就是帮蒟蒻HansBug理理思维。假设HansBug的思维是一长串字符串(字符串中包含且仅包含26个字母),现在的你,有一张神奇的药方,上面依次包含了三种操作:

获取第x到第y个字符中字母k出现了多少次
将第x到第y个字符全部赋值为字母k
将第x到第y个字符按照A-Z的顺序排序

你欣喜若狂之时,可是他脑细胞和RP已经因为之前过度紧张消耗殆尽,眼看试卷最后还有一篇800字的作文呢,所以这个关键的任务就交给你啦!

输入格式:

第一行包含两个整数N、M,分别表示HansBug的思维所包含的字母个数和药方上操作个数。
第二行包含一个长度为N的字符串,表示HansBug的思维。
第3-M+2行每行包含一条操作,三种操作格式如下:

操作1: 1 xi yi ki 表示将第xi到第yi个字符中ki出现的次数输出
操作2: 2 xi yi ki 表示将第xi到第yi个字符全部替换为ki
操作3: 3 xi yi 表示将第xi到第yi个字符按照A-Z的顺序排序

输出格式:

输出为若干行,每行包含一个整数,依次为所有操作1所得的结果。

说明

此题目中大小写不敏感。
在这里插入图片描述


正解线段树

开26棵线段树,分别记录每个字母出现的位置
比如序列中位置 i i 为字母 K i K_i ,那么就在 K i K_i 的线段树中第 i i 的位置置为1
对每个线段树维护一个区间和 s u m sum

操作1,直接在 K i K_i 的线段树里查询区间 [ x i , y i ] [x_i,y_i] 的和
操作2,把 K i K_i 的线段树中区间 [ x i , y i ] [x_i,y_i] 区间赋值为1,其他线段树中区间 [ x i , y i ] [x_i,y_i] 区间赋值为0
操作3,查询每个字母在区间 [ x i , y i ] [x_i,y_i] 出现的次数,先把每棵线段树的区间 [ x i , y i ] [x_i,y_i] 都置为0,重新赋值就好

#include<iostream>
#include<cmath>
#include<algorithm>
#include<queue>
#include<cstring>
#include<cstdio>
using namespace std;

int read()
{
    int f=1,x=0;
    char ss=getchar();
    while(ss<'0'||ss>'9'){if(ss=='-')f=-1;ss=getchar();}
    while(ss>='0'&&ss<='9'){x=x*10+ss-'0';ss=getchar();}
    return f*x;
}

const int maxn=100010;
int n,m;
char ss[maxn];
int sum[maxn<<2][28];
int sett[maxn<<2][28],cnt[50];

void add(int u,int s,int t,int p,int d)
{
    if(s==t){ sum[p][d]++; return;}
    int mid=s+t>>1;
    if(u<=mid) add(u,s,mid,p<<1,d);
    else add(u,mid+1,t,p<<1|1,d);
    sum[p][d]=sum[p<<1][d]+sum[p<<1|1][d];
}

void push(int s,int t,int mid,int p,int d)
{
    if(sett[p][d]==-1) return;
    sett[p<<1][d]=sett[p<<1|1][d]=sett[p][d];
    sum[p<<1][d]=(mid-s+1)*sett[p][d];
    sum[p<<1|1][d]=(t-mid)*sett[p][d];
    sett[p][d]=-1;
}

void update(int ll,int rr,int s,int t,int p,int d,int v)
{
    if(ll<=s&&t<=rr){ sum[p][d]=(t-s+1)*v; sett[p][d]=v; return;}
    int mid=s+t>>1;
    push(s,t,mid,p,d);
    if(ll<=mid) update(ll,rr,s,mid,p<<1,d,v);
    if(rr>mid) update(ll,rr,mid+1,t,p<<1|1,d,v);
    sum[p][d]=sum[p<<1][d]+sum[p<<1|1][d];
}

void assign(int ll,int rr,int d)
{
    for(int i=1;i<=26;++i)
    update(ll,rr,1,n,1,i,i==d);
}

int qsum(int ll,int rr,int s,int t,int p,int d)
{
    if(ll<=s&&t<=rr) return sum[p][d];
    int mid=s+t>>1,res=0;
    push(s,t,mid,p,d);
    if(ll<=mid) res+=qsum(ll,rr,s,mid,p<<1,d);
    if(rr>mid) res+=qsum(ll,rr,mid+1,t,p<<1|1,d);
    return res;
}

void ssort(int ll,int rr)
{
    memset(cnt,0,sizeof(cnt));
    for(int i=1;i<=26;++i)
    {
        cnt[i]=qsum(ll,rr,1,n,1,i);
        update(ll,rr,1,n,1,i,0);
    }
    for(int i=1;i<=26;++i)
    if(cnt[i]!=0)
    update(ll,ll+cnt[i]-1,1,n,1,i,1),ll+=cnt[i];
}

int main()
{
    n=read();m=read();
    scanf("%s",&ss);
    for(int i=0;i<n;++i)
    {
    	ss[i]=toupper(ss[i]);//记得同一转换为大或小写
        add(i+1,1,n,1,ss[i]-'A'+1);
    }
    
    memset(sett,-1,sizeof(sett));
    while(m--)
    {
        int k=read(),ll=read(),rr=read();
        if(k==1||k==2) scanf("%s",&ss),ss[0]=toupper(ss[0]);
        if(k==1) printf("%d\n",qsum(ll,rr,1,n,1,ss[0]-'A'+1));
        else if(k==2) assign(ll,rr,ss[0]-'A'+1);
        else if(k==3) ssort(ll,rr);
    }
    return 0;
}

总时空消耗量 5775ms / 55.46MB


非正解ODT珂朵莉树

暴力艹标程系列

骗分导论–ODT珂朵莉树

前两个很基本的ODT操作

操作3用桶记录每个字母的出现次数
set中删除对应区间再重新添加

#include<iostream>
#include<cmath>
#include<algorithm>
#include<vector>
#include<cstring>
#include<cstdio>
#include<set>
using namespace std;
#define IT set<node>::iterator 

int read()
{
    int f=1,x=0;
    char ss=getchar();
    while(ss<'0'||ss>'9'){if(ss=='-')f=-1;ss=getchar();}
    while(ss>='0'&&ss<='9'){x=x*10+ss-'0';ss=getchar();}
    return f*x;
}

const int maxn=100010;
int n,m,cnt[50];
char ss[maxn];
struct node
{
    int ll,rr;
    mutable int val;
    node(int L,int R=-1,int V=0): ll(L), rr(R), val(V) {}
    bool operator < (const node& tt) const {return ll<tt.ll;} 
};
set<node> st;

IT split(int pos)
{
    IT it=st.lower_bound(node(pos));
    if(it!=st.end()&&it->ll==pos) return it;
    --it;
    int ll=it->ll,rr=it->rr,val=it->val;
    st.erase(it);
    st.insert(node(ll,pos-1,val));
    return st.insert(node(pos,rr,val)).first;
}

void assign(int ll,int rr,int val)
{
    IT itr=split(rr+1),itl=split(ll);
    st.erase(itl,itr);
    st.insert(node(ll,rr,val));
}

int qsum(int ll,int rr,int k)
{
    int res=0;
    IT itr=split(rr+1),itl=split(ll);
    for(;itl!=itr;++itl) 
    if(itl->val==k) res+=itl->rr-itl->ll+1;
    return res;
}

void ssort(int ll,int rr)
{
    memset(cnt,0,sizeof(cnt));
    IT itr=split(rr+1),itl=split(ll);
    IT it=itl;
    for(;it!=itr;++it) cnt[it->val]+=it->rr-it->ll+1;
    st.erase(itl,itr);
    
    for(int i=1;i<=26;++i)
    if(cnt[i]!=0) 
    {
        st.insert(node(ll,ll+cnt[i]-1,i));
        ll+=cnt[i];
    }
}

int main()
{
    n=read();m=read();
    scanf("%s",&ss);
    for(int i=0;i<n;++i)
    {
        ss[i]=toupper(ss[i]);
        st.insert(node(i+1,i+1,ss[i]-'A'+1));
    }
    
    while(m--)
    {
        int k=read(),ll=read(),rr=read();
        if(k==1||k==2){ scanf("%s",&ss); ss[0]=toupper(ss[0]);}
        if(k==1) printf("%d\n",qsum(ll,rr,ss[0]-'A'+1));
        else if(k==2) assign(ll,rr,ss[0]-'A'+1);
        else if(k==3) ssort(ll,rr);
    } 
    return 0;
}

总时空消耗量 1289ms / 3.79MB

艹过正解不止一个数量级啊

猜你喜欢

转载自blog.csdn.net/niiick/article/details/83062725
今日推荐