国际惯例的题面:
考虑如果没有强制在线我们能怎么水掉这个题,先构造出字符串,各种方法求一下后缀数组,然后线段树维护区间rank最小的位置即可。
然而他要求强制在线,支持插入后缀,并比较后缀大小(求rank)的数据结构,当然就是后缀平衡树啦。
于是插入字符串的操作,我们只需要在后缀平衡树上插入这个后缀。此时不需要对线段树进行修改,因为线段树中任何一个位置均不包含新插入的这个后缀(保证信息合法)。
什么你说插入会改变每个后缀的rank值?没关系我们不需要知道每个后缀的rank具体是多少,我们只需要它们的相对大小关系,这个显然是不会改变的是吧。
(你还不明白?先去A了"Bzoj3600: 没有人的算术"再说。什么你A了还不明白?丢人!褪裙吧!)
然后对线段树的修改和查询就显然了。
(话说这题treap不旋比旋转快,替罪羊alpha设为1最快,某大佬裸BST直接AC都是什么鬼啊)
代码:
1 #include<cstdio> 2 #include<algorithm> 3 #include<cctype> 4 const int maxe=1e6+1e2; 5 const double alpha = 0.85; 6 7 char in[maxe]; 8 int at[maxe]; // suffix i's node . 9 double v[maxe]; 10 11 struct SuffixBalancedTree { // we should insert 0 as the minimal suffix . 12 int lson[maxe],rson[maxe],siz[maxe],sf[maxe],root,cnt; 13 int seq[maxe],sql; 14 int fail,failfa; 15 double vfl,vfr; 16 17 inline bool cmp(int x,int y) { 18 if( !x || !y ) return !x; 19 if( in[x] != in[y] ) return in[x] < in[y]; 20 else return v[at[x-1]] < v[at[y-1]]; 21 } 22 inline void upgrade(int pos,double l,double r) { 23 siz[pos] = siz[lson[pos]] + siz[rson[pos]] + 1; 24 if( std::max( siz[lson[pos]] , siz[rson[pos]] ) > siz[pos] * alpha ) fail = pos , failfa = -1 , vfl = l , vfr = r; 25 else if( fail == lson[pos] || fail == rson[pos] ) failfa = pos; 26 } 27 inline void insert(int &pos,double l,double r,const int &id) { 28 if( !pos ) { 29 v[at[id]=pos=++cnt]= ( l + r ) / 2.0 , siz[pos] = 1 , sf[pos] = id; 30 return; 31 } const double vmid = ( l + r ) / 2.0; 32 if( cmp(sf[pos],id) ) insert(rson[pos],vmid,r,id) , upgrade(pos,l,r); // id > sf[pos] . 33 else insert(lson[pos],l,vmid,id) , upgrade(pos,l,r); 34 } 35 inline int rebuild(int ll,int rr,double l,double r) { 36 const int mid = ( ll + rr ) >> 1 , pos = seq[mid]; 37 const double vmid = ( l + r ) / 2.0; v[pos] = vmid , siz[pos] = rr - ll + 1; 38 if( ll < mid ) lson[pos] = rebuild(ll,mid-1,l,vmid); 39 if( mid < rr ) rson[pos] = rebuild(mid+1,rr,vmid,r); 40 return pos; 41 } 42 inline void dfs(int pos) { 43 if(lson[pos]) dfs(lson[pos]); 44 seq[++sql] = pos; 45 if(rson[pos]) dfs(rson[pos]); 46 lson[pos] = rson[pos] = siz[pos] = 0; 47 } 48 inline void insert(const int &id) { 49 fail = 0 , failfa = -1 , insert(root,0,1,id); 50 if(fail) { 51 sql = 0 , dfs(fail); 52 if( ~failfa ) { 53 if( fail == lson[failfa] ) lson[failfa] = rebuild(1,sql,vfl,vfr); 54 else rson[failfa] = rebuild(1,sql,vfl,vfr); 55 } else root = rebuild(1,sql,0,1); 56 } 57 } 58 }sbt; 59 60 int cov[maxe>>1]; 61 62 struct SegmentTree { 63 int mx[maxe<<1]; 64 #define lson(pos) (pos<<1) 65 #define rson(pos) (pos<<1|1) 66 inline bool cmp(int a,int b) { 67 if( cov[a] == cov[b] ) return a < b; 68 return v[at[cov[a]]] < v[at[cov[b]]]; 69 } 70 inline void upgrade(int pos) { 71 mx[pos] = cmp(mx[lson(pos)],mx[rson(pos)]) ? mx[lson(pos)] : mx[rson(pos)]; 72 } 73 inline void build(int pos,int l,int r) { 74 if( l == r ) return void( mx[pos] = l ); 75 const int mid = ( l + r ) >> 1; 76 build(lson(pos),l,mid) , build(rson(pos),mid+1,r) , upgrade(pos); 77 } 78 inline void update(int pos,int l,int r,const int &tar) { 79 if( l == r ) return; // nothing to update . 80 const int mid = ( l + r ) >> 1; 81 if( tar <= mid ) update(lson(pos),l,mid,tar); 82 else update(rson(pos),mid+1,r,tar); 83 upgrade(pos); 84 } 85 inline int query(int pos,int l,int r,const int &ll,const int &rr) { 86 if( ll <= l && r <= rr ) return mx[pos]; 87 const int mid = ( l + r ) >> 1; 88 if( rr <= mid ) return query(lson(pos),l,mid,ll,rr); 89 else if( ll > mid ) return query(rson(pos),mid+1,r,ll,rr); 90 const int ql = query(lson(pos),l,mid,ll,rr) , qr = query(rson(pos),mid+1,r,ll,rr); 91 return cmp(ql,qr) ? ql : qr; 92 } 93 }sgt; 94 95 inline char nextchar() { 96 static const int BS = 1 << 21; 97 static char buf[BS],*st=buf+BS,*ed=st; 98 if( st == ed ) ed = buf + fread(st=buf,1,BS,stdin); 99 return st == ed ? -1 : *st++; 100 } 101 inline void getstr(char* s) { 102 char c; 103 while( !isalpha(c=nextchar()) ); 104 do *s++=c; while( isalpha(c=nextchar()) ); 105 } 106 inline char realchar() { 107 char c; 108 while( !isalpha(c=nextchar()) ); 109 return c; 110 } 111 inline int getint() { 112 int ret = 0 , ch; 113 while( !isdigit(ch=nextchar()) ); 114 do ret=ret*10+ch-'0'; while( isdigit(ch=nextchar()) ); 115 return ret; 116 } 117 118 int main() { 119 static int n,m,len,tpe,lastans; 120 n = getint() , m = getint() , len = getint() , tpe = getint() , getstr(in+1) , std::reverse(in+1,in+1+len); 121 for(int i=0;i<=len;i++) in[i] -= 'a' , sbt.insert(i); 122 for(int i=1;i<=n;i++) cov[i] = getint(); 123 sgt.build(1,1,n); 124 for(int i=1,o,c,x,l,r;i<=m;i++) { 125 o = realchar(); 126 if( o == 'I' ) { 127 c = getint(); 128 if( tpe ) c ^= lastans; 129 in[++len] = c , sbt.insert(len); 130 } else if( o == 'C' ) x = getint() , cov[x] = getint() , sgt.update(1,1,n,x); 131 else if( o == 'Q' ) l = getint() , r = getint() , printf("%d ",lastans=sgt.query(1,1,n,l,r)); 132 } 133 return 0; 134 }
思い出して 優しいハウリング
回忆起 温柔的振鸣
奏でる声 未来を示してた
演奏之声 昭示了未来
思い出して 優しいハウリング
回忆起 温柔的共鸣
遅くはない そう教えてくれた
永远不会太迟 你是这样教我的
今までの過ちすべて
至今经历的一切
巻き戻すことなんて出来はしない
已不能倒带
間違いを認める勇気
不懂得承认错误的勇气
知らなかったよ 凄く不器用に 生きてた
十分笨拙的生存着