xsy2353. A Good Problem

给定 \(m\) 进制下的 \(l,r\) 以及 \(n\) 个数 \(\text{num}_i\),每个数有一个权值 \(v_i\)。定义一个 \(m\) 进制数 \(x\) 的价值为 \(\sum r_i\cdot v_i\),其中 \(r_i\)\(\text{num}_i\)\(x\) 中出现的次数\((\text{num}_i\) 中的前导零要匹配,\(x\) 中的前导零不匹配\()\),求 \([l,r]\) 中价值 \(\le k\) 的数的数量,答案对 \(10^9+7\) 取模。
\(1\le n\le 200,2\le m\le 20,1\le k\le 500,1\le l,r\) 的位数\(,n\) 个数的总位数\(,v_i \le 200\)


考虑按照数位一位一位加入 \(x\),统计每个数出现的次数与建出 \(n\) 个数的 \(\text{Fail}\) 树后在 \(\text{AC}\) 自动机上作匹配的过程相同。

预处理出 \(\text{AC}\) 自动机上每个节点的权值和,因为根到 \(fail_u\) 的路径组成的数是根到 \(u\) 的路径组成的数的后缀,所以 \(u\) 的权值要加上 \(fail_u\) 的权值。

接着做个正常的数位 \(dp\),记录当前确定到的位数、在 \(\text{AC}\) 自动机上的哪个节点、是否达到上限、权值和、到目前为止是否都是前导零即可。

总时间复杂度 \(O(nk\sum |num_i|)\),其中 \(|x|\)\(x\) 的位数。

#include<bits/stdc++.h>
using namespace std;
const int N=201,mod=1e9+7;
struct node{int son[20],fail,val;}trie[N*10];
int n,m,k,cnt=1,pos,L1,L2,L[N],R[N],num[N],dp[N][N][501];
bool vis[N][N][501];queue<int>q;
inline void insert(int *s,int len,int v){
	int rt=1;
	for(int i=1;i<=len;++i){
		if(!trie[rt].son[s[i]])trie[rt].son[s[i]]=++cnt;
		rt=trie[rt].son[s[i]];
	}
	trie[rt].val+=v;
}
inline void getFail(){
	for(int i=0;i<m;++i)trie[0].son[i]=1;
	q.push(1);
	while(!q.empty()){
		int u=q.front();q.pop();
		for(int i=0;i<m;++i){
			int v=trie[u].son[i],Fail=trie[u].fail;
			if(!v){trie[u].son[i]=trie[Fail].son[i];continue;}
			trie[v].fail=trie[Fail].son[i];
			trie[v].val+=trie[trie[v].fail].val;
			q.push(v);
		}
	}
}
inline int digit_dp(int len,int *num,int u,bool lim,int val,int non){
	if(val>k)return 0;
	if(!len){
		if(!non)return trie[trie[1].son[0]].val<=k;
		return 1;
	}
	if(non&&!lim&&vis[len][u][val])return dp[len][u][val];
	int ans=0,mx=lim?num[len]:m-1;
	for(int i=0;i<=mx;++i){
		int v=non||i>0?trie[u].son[i]:u;
		(ans+=digit_dp(len-1,num,v,lim&&i==num[len],val+trie[v].val,non||i>0))%=mod;
	}
	if(non&&!lim)vis[len][u][val]=1,dp[len][u][val]=ans;
	return ans;
}
int main(){
	scanf("%d%d%d",&n,&m,&k);
	scanf("%d",&L1);
	for(int i=L1;i;--i)scanf("%d",L+i);
	scanf("%d",&L2);
	for(int i=L2;i;--i)scanf("%d",R+i);
	for(int i=1,len,v;i<=n;++i){
		scanf("%d",&len);
		for(int j=1;j<=len;++j)scanf("%d",num+j);
		scanf("%d",&v),insert(num,len,v);
	}
	getFail();
	if(!L1)return printf("%d\n",digit_dp(L2,R,1,1,0,0)),0;
	for(int i=1;i<=L1;++i)if(L[i]){pos=i;break;}
	--L[pos];for(int i=1;i<pos;++i)L[i]=m-1;
	while(!L[L1]&&L1)--L1;
	return printf("%d\n",(digit_dp(L2,R,1,1,0,0)-digit_dp(L1,L,1,1,0,0)+mod)%mod),0;
}
posted @ 2022-06-05 15:17  Samsara-soul  阅读(29)  评论(0编辑  收藏  举报