时空限制 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
i为字母
K
i
K_i
Ki,那么就在
K
i
K_i
Ki的线段树中第
i
i
i的位置置为1
对每个线段树维护一个区间和
s
u
m
sum
sum
操作1,直接在
K
i
K_i
Ki的线段树里查询区间
[
x
i
,
y
i
]
[x_i,y_i]
[xi,yi]的和
操作2,把
K
i
K_i
Ki的线段树中区间
[
x
i
,
y
i
]
[x_i,y_i]
[xi,yi]区间赋值为1,其他线段树中区间
[
x
i
,
y
i
]
[x_i,y_i]
[xi,yi]区间赋值为0
操作3,查询每个字母在区间
[
x
i
,
y
i
]
[x_i,y_i]
[xi,yi]出现的次数,先把每棵线段树的区间
[
x
i
,
y
i
]
[x_i,y_i]
[xi,yi]都置为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
艹过正解不止一个数量级啊