2019 ICPC Asia Nanjing RegionalF. Paper Grading字典树上的dfs序上建立树状数组套权值线段树

    科技2024-06-19  73

    这题的关键思路点:

    把n个串插入到字典树上后,对于一个串t,与其前缀k个相同的在字典树的字符串为:  从根节点沿着t走k步后到达的节点tk,其子树节点满足这个条件。

    然后自然而然就能想到字典树上dfs序建立主席树,求l-r字符串中,符合条件的字符串个数。(主席树的经典操作了,求[l,r]范围内值为x-y,的数的个数。)

    即建立n棵树,每棵树i,的位置j,维护的是:1-i字符串中,dfs序为j的字符串的个数。

    由于要支持修改操作,我们可以用树状数组套权值线段树:

    具体操作见代码:

    (写了个空间回收机制,当内存不够用时可以加上!!!)

    #include <bits/stdc++.h> using namespace std; typedef long long ll; #define re register #define pb push_back typedef pair<int,int> pii; const double PI= acos(-1.0); const int M = 4e5+7; char s[M]; int ti[M][26],CNT=1; vector<int> mp[M];//当前字典树节点,是哪些字符串结尾节点 int id[M]; void in(char *s,int tp){//插入到字典树中 int n=strlen(s),p=1; for(int i=0;i<n;i++){ int ch=s[i]-'a'; if(!ti[p][ch])ti[p][ch]=++CNT; p=ti[p][ch]; } mp[p].pb(tp);//以当前节点结尾的字符串的 } int st[M],ed[M],sz; void dfs(int x){ st[x]=++sz; int l=mp[x].size(); // cout<<x<<" - - "<<l<<" "<<CNT<<endl; if(l){ id[mp[x][0]]=sz; for(int i=1;i<l;i++)id[mp[x][i]]=++sz; } for(int i=0;i<26;i++){ //cout<<x<<" "<<ti[x][i]<<endl; if(ti[x][i])dfs(ti[x][i]); } ed[x]=sz; } int rt[M],tr[M*50],ls[M*50],rs[M*50],cnt,n; /* vector<int>rb;//垃圾桶 void gb(int o){ tr[o]=0;//把节点丢掉的同时也要把节点里的值给清空 rb.pb(o); if(ls[o])gb(ls[o]),ls[o]=0; if(rs[o])gb(rs[o]),rs[o]=0; }*/ void up(int &o,int l,int r,int w,int op){ if(!o)o=++cnt; tr[o]+=op; //垃圾回收机制,当卡空间,而不卡时间时可以加上下面的操作 /*if(!o){ if(rb.size())o=rb[rb.size()-1],rb.pop_back(); else o=++cnt; } tr[o]+=op; if(tr[o]==0){//当前节点所维护的区间值为0,即"无用"可以当垃圾扔掉 gb(o);o=0; return ;///后面就不用操作了,反正区间和都是0 }*/ if(l==r)return; int m=(l+r)/2; if(w<=m)up(ls[o],l,m,w,op); else up(rs[o],m+1,r,w,op); }// void ADD(int pos,int w,int op){ for(int i=pos;i<=n;i+=(i&-i))//第一维树状数组对应的是n个位置 up(rt[i],1,sz,w,op); } int qu(int o,int l,int r,int x,int y){ if(!o||tr[o]==0) return 0; if(x<=l&&r<=y) return tr[o]; int m=(l+r)>>1,res=0; if(x<=m) res+=qu(ls[o],l,m,x,y); if(y>m) res+=qu(rs[o],m+1,r,x,y); return res; } int QU(int l,int r,int x,int y){ int ans=0; for(int i=r;i;i-=(i&-i))ans+=qu(rt[i],1,sz,x,y); for(int i=l-1;i;i-=(i&-i))ans-=qu(rt[i],1,sz,x,y); return ans; } int main() { ios::sync_with_stdio(false); cin.tie(0); int q; cin>>n>>q; for(int i=1;i<=n;i++){ cin>>s; in(s,i); } dfs(1); for(int i=1;i<=n;i++)//单点加 ADD(i,id[i],1); //cout<<i<<" "<<id[i]<<endl;; while(q--){ int opt,k,l,r; cin>>opt; if(opt==1){ cin>>l>>r;//交换i,j字符串 ADD(l,id[l],-1);ADD(r,id[r],-1); ADD(l,id[r],1);ADD(r,id[l],1); swap(id[l],id[r]); } else{ cin>>s; cin>>k>>l>>r;//查询id 为l,r字符串中与 s的公共前缀至少为k的个数 int p=1; for(int i=0;i<k;i++) p=ti[p][s[i]-'a']; if(p==0) cout<<0<<endl; else{ int ans=QU(l,r,st[p],ed[p]); cout<<ans<<endl; } } } return 0; }

     

    Processed: 0.018, SQL: 8