CF558E A Simple Task
阿新 • • 發佈:2020-12-25
Link
Solution
和之前有道省選題很像,同樣是用線段樹來實現排序操作。但這道題不同之處在於值域很小,以致於每個數的個數可以直接分別用線段樹統計出來,這正是這道題的突破口。後面的就很好想了,每次對一個區間排序只需要統計所有字母的出現次數,然後按順序區間覆蓋,易知複雜度是 \(O(n\log{n})\) 的,帶一個 26 的大常數(顯然跑不滿)。
#include<stdio.h> #include<string.h> #define N 100007 #define lid id<<1 #define rid id<<1|1 inline int read(){ int x=0,flag=1; char c=getchar(); while(c<'0'||c>'9'){if(c=='-') flag=0;c=getchar();} while(c>='0'&&c<='9'){x=(x<<1)+(x<<3)+c-48;c=getchar();} return flag? x:-x; } struct Node{ int s[30],cov,l,r,mid; }t[N<<2]; char s[N]; int n,m; void merge(Node &id,Node x,Node y){ for(int i=1;i<=26;i++) id.s[i]=x.s[i]+y.s[i]; } void build(int id,int lf,int rf){ t[id].l=lf,t[id].r=rf; if(lf==rf) t[id].s[s[lf]-'a'+1]=1; else{ t[id].mid=(lf+rf)>>1; build(lid,lf,t[id].mid); build(rid,t[id].mid+1,rf); merge(t[id],t[lid],t[rid]); } } void push(int id,int val){ t[id].cov=val; for(int i=1;i<=26;i++) t[id].s[i]=0; t[id].s[val]=t[id].r-t[id].l+1; } void pushdown(int id){ push(lid,t[id].cov); push(rid,t[id].cov); t[id].cov=0; } int l,r,val; Node query(int id){ if(l<=t[id].l&&t[id].r<=r) return t[id]; if(t[id].cov) pushdown(id); if(r<=t[id].mid) return query(lid); else if(l>t[id].mid) return query(rid); Node tmp; merge(tmp,query(lid),query(rid)); return tmp; } void modify(int id){ if(l<=t[id].l&&t[id].r<=r) push(id,val); else{ if(t[id].cov) pushdown(id); if(l<=t[id].mid) modify(lid); if(r>t[id].mid) modify(rid); merge(t[id],t[lid],t[rid]); } } void print(){ for(int i=1;i<=n;i++){ l=r=i; Node ret=query(1); for(int i=1;i<=26;i++) if(ret.s[i]) putchar('a'+i-1); } // putchar('\n'); } int main(){ n=read(),m=read(); scanf("%s",s+1); build(1,1,n); int op; for(int i=1;i<=m;i++){ l=read(),r=read(),op=read(); Node ret=query(1); if(op){ int l_,r_=l-1; for(int i=1;i<=26;i++){ if(!ret.s[i]) continue; l=r_+1,r=r_+ret.s[i],val=i,modify(1); r_=r; } }else{ int l_,r_=l-1; for(int i=26;i;i--){ if(!ret.s[i]) continue; l=r_+1,r=r_+ret.s[i],val=i,modify(1); r_=r; } } // print(); } print(); }