题意:输入一个由0-9组成的字符串A,给定一组转换规则S,S[i]把数字i map到一个新的string,如此可将A一层一层扩展,直到长度>=M为止,给定一组query,求扩展后的字符串的区间和。
这一题其实也没那么难,但是标了个hard,让人不战而败。。感觉自己还是不够confident + aggressive ε=(´ο`*)))唉
扩展的过程类似于生成一棵树,第一层节点是A,然后每个节点逐层扩展。一个observation是因为相同数字扩展规则相同,所以如果A[i]=A[j],那么扩展后A[i]和A[j]对应的子树是一样的。同理,同一层相同的节点扩展后的子树也是一样得。
求区间和等价于求前缀和,求[0,...,l]的前缀和,需要每个子树所拥有的叶子个数,以及对应的叶子value之和。
因为每一层不同的节点最多只有10个,所以用fnum[10][maxk]记录叶子个数,fnum[i][k]表示第k层数字i对应的子树叶子节点个数。另外,用fsum[10][maxk]记录叶子value之和,fsum[i][k]表示第k层数字i对应的子树叶子节点value之和。
fnum[i][k]和fsum[i][k]可以用树dp(记忆化搜索)求得,树是递归结构,感觉很多问题都是递归求解。fnum[i][k]=sum_j fnum[j][k+1],fsum[i][k]=sum_j fsum[j][k+1], j is the number expanted from i。递归出口是在最大层(叶子层)时,fnum[i][k]=1,fsum[i][k]=i。
求前缀和类似于线段树从顶端往下搜索,先对于fnum[A[i]][0]进行二分查找,找出第0层l应该在那一个子树中,如果在第i个子树中,返回presum of fsum[0,...,i-1]+dfs(l-presum of fnum[0,...,i-1])。对于子树中的每个node,也是找到第一个child使得其fnum[i][k]前缀和刚好>l,再从该child对应的子树中搜索,传入的参数l应该减去child之前的子树叶子节点数之和。递归出口是最大层是返回1,此时找到了对应的l,也恰巧l=1。
开始WA是因为理解错了题意,以为S[i]长度都一样。。结束后可以看test case。。然后发现咦居然还有这种input。。
给定M,求最大层数maxlvel也可以由逐层扩展模拟实现。第k层记录0-9出现的频次,然后k+1层可以根据拓展规则S求出第k层的数字i会生成0-9的数字频次是多少,之后相加即可。
#include<iostream>
#include<stdio.h>
#include<cstdio>
#include<string>
#include<cmath>
#include<stdlib.h>
#include<algorithm>
#include<string.h>
#include<cstring>
#include<vector>
#include<queue>
#include<map>
#include<set>
#include<ctime>
#include<list>
using namespace std;
const int maxn=20;
int T;
int N;
int M;
int Q;
vector<long>presum;
vector<long>presumsum;
string Ag;
vector<string>dict;
int L;
int R;
const int maxk=1000;//max level of tree
long fnum[maxn][maxk];
long fsum[maxn][maxk];
bool vis[maxn][maxk];
int maxlevel;
long calc_sum_leaf(int num,int k)
{
if(vis[num][k]==true)
{
return fsum[num][k];
}
if(k==maxlevel)
{
fsum[num][k]=num;
vis[num][k]=true;
return num;
}
else
{
long childsum=0;
for(int i=0;i<dict[num].length();i++)
{
childsum+=calc_sum_leaf(dict[num][i]-'0',k+1);
}
fsum[num][k]=childsum;
vis[num][k]=true;
return fsum[num][k];
}
}
long calc_leaf_num(int num,int k)
{
if(vis[num][k]==true)
{
return fnum[num][k];
}
if(k==maxlevel)
{
fnum[num][k]=1;
vis[num][k]=true;
// cout<<"num "<<num<<" level "<<k<<" "<<fnum[num][k]<<endl;
return 1;
}
else
{
long childnum=0;
for(int i=0;i<dict[num].length();i++)
{
childnum+=calc_leaf_num(dict[num][i]-'0',k+1);
}
fnum[num][k]=childnum;
vis[num][k]=true;
// cout<<"num "<<num<<" level "<<k<<" "<<fnum[num][k]<<endl;
return fnum[num][k];
}
}
void initialize(int n, long m, string A, vector<string> S) {
// m=1e17;
// cout<<m<<endl;
dict.clear();
presum.clear();
presumsum.clear();
for(int i=0;i<S.size();i++)
{
dict.push_back(S[i]);
}
Ag=A;
long long cnt=n;
long numfreq[2][maxn];//rolling, one is current, one is next
long currnumfreq[maxn][maxn];//[i,j] num i will leads xxx # of j to appear in the expansion
memset(numfreq,0,sizeof(numfreq));
memset(currnumfreq,0,sizeof(currnumfreq));
for(int i=0;i<10;i++)
{
// cout<<"num "<<i<<endl;
for(int j=0;j<dict[i].size();j++)
{
currnumfreq[i][dict[i][j]-'0']++;
}
// for(int j=0;j<10;j++)
// {
// cout<<currnumfreq[i][j]<<" ";
// }
// cout<<endl;
}
int curr=0;
for(int i=0;i<A.length();i++)
{
numfreq[curr][A[i]-'0']++;
}
cnt=A.length();
for(int i=0;i<maxk;i++)
{
// cout<<"level "<<i<<" cnt "<<cnt<<endl;
if(cnt>=m)
{
maxlevel=i;
break;
}
cnt=0;
int nextcurr=(curr+1)%2;
memset(numfreq[nextcurr],0,sizeof(numfreq[nextcurr]));
for(int j=0;j<10;j++)
{
// cout<<"num "<<j<<" "<<numfreq[curr][j]<<endl;
if(numfreq[curr][j]==0)
{
continue;
}
else
{
for(int k=0;k<10;k++)
{
numfreq[nextcurr][k]+=numfreq[curr][j]*currnumfreq[j][k];
// cout<<"Add "<<k<<" count "<<numfreq[curr][j]*currnumfreq[j][k]<<endl;
}
}
}
for(int j=0;j<10;j++)
{
cnt+=numfreq[nextcurr][j];
}
curr=nextcurr;
}
// long expand=S[0].length();//different S[i] may have different length
// for(int i=0;i<maxk;i++)
// {
cout<<i<<" "<<cnt<<endl;
// if(cnt>=m)
// {
// maxlevel=i;
// break;
// }
// cnt*=expand;
// }
//maxlevel=60;
cout<<"max level "<<maxlevel<<endl;
memset(fnum,0,sizeof(fnum));
memset(fsum,0,sizeof(fsum));
memset(vis,false,sizeof(vis));
for(int i=0;i<A.length();i++)
{
calc_sum_leaf(A[i]-'0',0);
}
// cout<<"here0"<<endl;
// for(int j=0;j<=maxlevel;j++)
// {
// for(int i=0;i<10;i++)
// {
// cout<<"num "<<i<<" level "<<j<<" "<<fsum[i][j]<<" "<<endl;
// }
// cout<<endl;
// }
memset(vis,false,sizeof(vis));
for(int i=0;i<A.length();i++)
{
// cout<<"calc num of "<<A[i]<<endl;
calc_leaf_num(A[i]-'0',0);
}
// cout<<"here1"<<endl;
// for(int j=0;j<=maxlevel;j++)
// {
// for(int i=0;i<10;i++)
// {
// cout<<"num "<<i<<" level "<<j<<" "<<fnum[i][j]<<" "<<endl;
// }
// cout<<endl;
// }
//presum of node in the level 0
presum.push_back(fnum[A[0]-'0'][0]);
presumsum.push_back(fsum[A[0]-'0'][0]);
for(int i=1;i<A.length();i++)
{
presum.push_back(presum[i-1]+fnum[A[i]-'0'][0]);
presumsum.push_back(presumsum[i-1]+fsum[A[i]-'0'][0]);
}
// cout<<"presum"<<endl;
// for(int i=0;i<presum.size();i++)
// {
// cout<<presum[i]<<" ";
// }
// cout<<endl;
// cout<<"here2"<<endl;
// for(int i=0;i<presumsum.size();i++)
// {
// cout<<presumsum[i]<<" ";
// }
// cout<<endl;
}
long dfs(long idx,int num,int k)
{
// cout<<idx<<" "<<num<<" "<<k<<endl;
if(k==maxlevel)
{
return (long)num;
}
long ret=0;
for(int i=0;i<dict[num].size();i++)
{
int next=dict[num][i]-'0';
if(fnum[next][k+1]<idx)
{
ret+=fsum[next][k+1];
idx-=fnum[next][k+1];
}
else
{
ret+=dfs(idx,next,k+1);
break;
}
}
return ret;
}
long getpresum(long idx)
{
if(idx==0)
{
return 0;
}
// cout<<"search idx "<<idx<<endl;
int st=lower_bound(presum.begin(),presum.end(),idx)-presum.begin();
// cout<<"start from "<<st<<endl;
if(st==0)
{
return dfs(idx,Ag[st]-'0',0);
}
//start from sub tree in level 0;
else
{
// cout<<"pre sub tree "<<fsum[Ag[st-1]-'0'][0]<<endl;
return presumsum[st-1]+dfs(idx-presum[st-1],Ag[st]-'0',0);
}
}
long query(long l, long r) {
return getpresum(r)-getpresum(l-1);
}
int main()
{
// list<char>arr;
// for(int i=0;i<5;i++)
// {
// arr.push_back(i+'0');
// }
// for(list<int>::iterator iter=arr.begin();iter!=arr.end();)
// {
// if((*iter)==2)
// {
// iter=arr.erase(iter);
// cout<<(*iter)<<endl;
// break;
// }
// else
// {
// iter++;
// }
arr.insert(iter,10);
//
// }
// for(list<int>::iterator iter=arr.begin();iter!=arr.end();iter++)
// {
// cout<<(*iter)<<" ";
// }
// cout<<endl;
// return 0;
freopen("input.txt","r",stdin);
// freopen("out.txt","w",stdout);
// freopen("out-cmp.txt","w",stdout);
cin>>T;
for(int ca=1;ca<=T;ca++)
{
cin>>N>>M>>Q;
string A;
vector<string>S;
cin>>A;
cin.ignore();
S.clear();
// cout<<N<<" "<<M<<" "<<Q<<endl;
for(int i=0;i<10;i++)
{
string tmp;
cin>>tmp;
cin.ignore();
// cout<<"tmp "<<tmp<<"ed"<<endl;
S.push_back(tmp);
}
// cin.ignore();
initialize(N,M,A,S);
for(int i=0;i<Q;i++)
{
cin>>L>>R;
// cout<<L<<" "<<R<<endl;
cout<<query(L,R)<<endl;
// cout<<"presum of R "<<R<<" "<<getpresum(R)<<endl;
}
// cout<<"Case #"<<ca<<": "<<query(L,R)<<endl;
}
return 0;
}
附上最初认怂用linked list暴力模拟的代码。。
#include<iostream>
#include<stdio.h>
#include<cstdio>
#include<string>
#include<cmath>
#include<stdlib.h>
#include<algorithm>
#include<string.h>
#include<cstring>
#include<vector>
#include<queue>
#include<map>
#include<set>
#include<ctime>
#include<list>
using namespace std;
const int maxn=10;
int T;
int N;
int M;
int Q;
vector<long>presum;
string A;
vector<string>S;
int L;
int R;
const int maxk=60;//max level of tree
void initialize(int n, long m, string A, vector<string> S) {
presum.clear();
// cout<<"A "<<A<<endl;
// for(int i=0;i<S.size();i++)
// {
// cout<<S[i]<<endl;
// }
// cout<<"end input"<<endl;
// vector<char>arr;
list<int>arr;
for(int i=0;i<n;i++)
{
arr.push_back(A[i]-'0');
}
// cout<<"here"<<endl;
int cnt=n;
while(cnt<m)
{
for(list<int>::iterator iter=arr.begin();iter!=arr.end();)
{
int tmp=*iter;
// cout<<tmp<<endl;
iter=arr.erase(iter);
// cout<<"after erase "<<(*iter)<<endl;
// arr.insert(iter,9);
for(int i=0;i<S[tmp].length();i++)
{
arr.insert(iter,S[tmp][i]-'0');
// cout<<"insert "<<S[tmp][i]<<endl;
}
cnt+=S[tmp].length()-1;
}
}
// for(list<int>::iterator iter=arr.begin();iter!=arr.end();iter++)
// {
// cout<<(*iter)<<" ";
// }
// cout<<endl;
list<int>::iterator iter=arr.begin();
presum.push_back(0);
for(int i=1;i<=cnt;i++)
{
// presum[i]=(*iter)+presum[i-1];
presum.push_back((*iter)+presum[i-1]);
iter++;
}
// for(int i=0;i<=cnt;i++)
// {
// cout<<presum[i]<<" ";
// }
// cout<<endl;
}
long query(long l, long r) {
// cout<<r<<" "<<l-1<<" "<<presum[r]<<" "<<presum[l-1]<<endl;
// for(int i=0;i<=M;i++)
// {
// cout<<presum[i]<<" ";
// }
// cout<<endl;
return presum[r]-presum[l-1];
}
int main()
{
// list<char>arr;
// for(int i=0;i<5;i++)
// {
// arr.push_back(i+'0');
// }
// for(list<int>::iterator iter=arr.begin();iter!=arr.end();)
// {
// if((*iter)==2)
// {
// iter=arr.erase(iter);
// cout<<(*iter)<<endl;
// break;
// }
// else
// {
// iter++;
// }
arr.insert(iter,10);
//
// }
// for(list<int>::iterator iter=arr.begin();iter!=arr.end();iter++)
// {
// cout<<(*iter)<<" ";
// }
// cout<<endl;
// return 0;
freopen("input.txt","r",stdin);
// freopen("out.txt","w",stdout);
// freopen("out-cmp.txt","w",stdout);
cin>>T;
for(int ca=1;ca<=T;ca++)
{
cin>>N>>M>>Q;
cin>>A;
cin.ignore();
S.clear();
// cout<<N<<" "<<M<<" "<<Q<<endl;
for(int i=0;i<10;i++)
{
string tmp;
cin>>tmp;
cin.ignore();
// cout<<"tmp "<<tmp<<"ed"<<endl;
S.push_back(tmp);
}
// cin.ignore();
initialize(N,M,A,S);
for(int i=0;i<Q;i++)
{
cin>>L>>R;
// cout<<L<<" "<<R<<endl;
cout<<query(L,R)<<endl;
}
cout<<"Case #"<<ca<<": "<<query(L,R)<<endl;
}
return 0;
}