题目:https://loj.ac/problem/2291
想了线段树合并的做法。就是用线段树维护 trie 的每个点在各种时间的操作。
然后线段树合并一番,线段树维护前缀最大值,就是维护最大子段和的套路,记录区间和、前缀 max 。查询的时候,因为当前区间只记录了自己区间内部的前缀 max 值,所以要加一个 pr 表示该区间前面的区间和。
空间可能爆? RE 就没管。后来发现是 go[ ][ ] 开成 N 而非 M 了。这个做法还是可过的。
注意强制在线的 ans 是带绝对值的。注意 mx 的初值是 0 而非 -INF 因为可以不选。
#include<cstdio> #include<cstring> #include<algorithm> #define ls Ls[cr] #define rs Rs[cr] using namespace std; int rdn() { int ret=0;bool fx=1;char ch=getchar(); while(ch>'9'||ch<'0'){if(ch=='-')fx=0;ch=getchar();} while(ch>='0'&&ch<='9')ret=ret*10+ch-'0',ch=getchar(); return fx?ret:-ret; } int Mx(int a,int b){return a>b?a:b;} int Mn(int a,int b){return a<b?a:b;} const int N=1e5+5,M=6e6+5,M2=2e7+5;//mention int n,cnt=1,go[M][10],rt[M],qnt;//M not N!!! int tot,Ls[M2],Rs[M2],sm[M2],mx[M2]; char ch[65]; struct Node{ int a,b,c,t;char s[65];}q[N]; int nwnd(int pr) { int cr=++tot; ls=Ls[pr]; rs=Rs[pr]; sm[cr]=sm[pr]; mx[cr]=mx[pr]; return cr; } void pshp(int cr)//ini mx is 0!!(can choosen't) { mx[cr]=Mx(mx[ls],sm[ls]+mx[rs]); sm[cr]=sm[ls]+sm[rs]; } void ins(int l,int r,int &cr,int p,int k) { if(!cr)cr=++tot; if(l==r){sm[cr]=mx[cr]=k;return;} int mid=l+r>>1; if(p<=mid)ins(l,mid,ls,p,k); else ins(mid+1,r,rs,p,k); pshp(cr); } void ins(int t,int k) { int cr=1,len=strlen(ch+1); for(int i=1,d;i<=len;i++) { d=ch[i]-'a'; if(!go[cr][d])go[cr][d]=++cnt; cr=go[cr][d]; } ins(1,n,rt[cr],t,k); } void mrg(int l,int r,int &cr,int pr) { if(!pr)return; if(!cr){cr=pr;return;} cr=nwnd(cr); int mid=l+r>>1; mrg(l,mid,ls,Ls[pr]); mrg(mid+1,r,rs,Rs[pr]); pshp(cr); } void dfs(int cr) { for(int i=0,v;i<10;i++) if(v=go[cr][i]) { dfs(v); mrg(1,n,rt[cr],rt[v]); } } int qry(int l,int r,int cr,int R,int lm,int pr) { if(pr+mx[cr]<=lm)return -1; if(l==r)return l; int mid=l+r>>1; if(mid>=R)return qry(l,mid,ls,R,lm,pr); if(pr+mx[ls]>lm)return qry(l,mid,ls,R,lm,pr); return qry(mid+1,r,rs,R,lm,pr+sm[ls]); } int qry(int bh,int lm) { int cr=1,len=strlen(q[bh].s+1); for(int i=1,d;i<=len;i++) { d=q[bh].s[i]-'a'; if(!go[cr][d])return -1;//-1 not 0!!!!! cr=go[cr][d]; } return qry(1,n,rt[cr],q[bh].t,lm,0); } int main() { n=rdn(); for(int i=1,op;i<=n;i++) { op=rdn(); if(op==1||op==2) { scanf("%s",ch+1); ins(i,(op==1?1:-1)); } else { qnt++; scanf("%s",q[qnt].s+1); q[qnt].a=rdn(); q[qnt].b=rdn(); q[qnt].c=rdn(); q[qnt].t=i; } } dfs(1); int ans=0; for(int i=1;i<=qnt;i++) { if(ans<0)ans=-ans;////// int k=((long long)q[i].a*ans+q[i].b)%q[i].c; ans=qry(i,k); printf("%d\n",ans); } return 0; }View Code
看了题解,原来 trie 每个节点开 vector 存 “超过某个值的最早时间” 即可。也不用往上合并,往下走的时候路径上的 vector 全加上即可。
#include<cstdio> #include<cstring> #include<algorithm> #include<vector> #define pb push_back using namespace std; int rdn() { int ret=0;bool fx=1;char ch=getchar(); while(ch>'9'||ch<'0'){if(ch=='-')fx=0;ch=getchar();} while(ch>='0'&&ch<='9')ret=ret*10+ch-'0',ch=getchar(); return fx?ret:-ret; } const int N=1e5+5,M=N*60; int n,go[M][10],tot=1,ct[M],ans; char ch[65]; vector<int> vt[M]; void ins(int t,int k) { int cr=1,len=strlen(ch+1); for(int i=1,d;i<=len;i++) { d=ch[i]-'a'; if(!go[cr][d])go[cr][d]=++tot; cr=go[cr][d]; ct[cr]+=k; if(ct[cr]>vt[cr].size()) vt[cr].pb(t); } } void qry(int k) { int cr=1,len=strlen(ch+1); for(int i=1,d;i<=len;i++) { d=ch[i]-'a'; if(!go[cr][d]){ans=-1;printf("%d\n",ans);return;} cr=go[cr][d]; } if(vt[cr].size()<=k)ans=-1; else ans=vt[cr][k]; printf("%d\n",ans); } int main() { n=rdn(); for(int i=1,op;i<=n;i++) { op=rdn();scanf("%s",ch+1); if(op==1||op==2)ins(i,(op==1?1:-1)); else { int a=rdn(),b=rdn(),c=rdn(); if(ans<0)ans=-ans; int k=((long long)a*ans+b)%c; qry(k); } } return 0; }