AC自动机上的DP CF433E Tachibana Kanade's Tofu

题目链接 

在m进制下两数L、R,给出若干子串(m进制),每个子串有一个权值。一个m进制数的辣度值为该数的字符串表示中出现已给子串的权值和,子串可以重复,即一个子串可以出现多次。

对给出的子串建立AC自动机,求任意数的辣度值在AC自动机上走一遍即可。所以在AC自动机上进行数位DP即可,DP[len][node][k][upper]表示自动机上接收长度为len,节点为node,辣度值为k,且接收字符串是否为模板的前len位时,串的个数。有m中转移方式到接受长度为len+1的状态。

import java.util.*;
import java.math.*;
import java.io.*;

public class Main {
	static int mod = 1000000007;
	static AC ac;
	static int n, m, K;
	static int[] readnum() throws IOException {
		int len = Reader.nextInt();
		int[] ret = new int[len];
		for (int i=0; i<len; i++)
			ret[i] = Reader.nextInt();
		return ret;
	}
	public static void main(String[] args) throws IOException {
//		System.setIn(new FileInputStream("injava.txt"));
		Reader.init(System.in);
		n = Reader.nextInt();
		m = Reader.nextInt();
		K = Reader.nextInt();
		int[] L = readnum();
		int[] R = readnum();
		ac = new AC(m);
		for (int i=0; i<n; i++) {
			int[] x = readnum();
			int val = Reader.nextInt();
			ac.insert(x, x.length, val);
		}
		ac.build();
		L[L.length - 1]--;
		for (int i=L.length-1; i>=0; i--) {
			if (L[i] < 0) {
				L[i] = m - 1;
				L[i-1]--;
			}
		}
		if (L[0] == 0)
			L = Arrays.copyOfRange(L, 1, L.length);
		System.out.println((ac.getans(R, n, m, K) - ac.getans(L, n, m, K) + mod) % mod);;
	}
}
class AC {
	int m, cnt;
	class Node{
		Node[] go;
		Node fail;
		int val, id;
		Node() {
			go = new Node[m];
			fail = null;
			val = 0;
			id = cnt++;
		}
	}
	Node root;
	Node[] node;
	AC(int _m) {
		m = _m;
		root = new Node();
	}
	void insert(int[] a, int len, int val) {
		Node now = root;
		for (int i=0; i<len; i++) {
			if (now.go[a[i]] == null)
				now.go[a[i]] = new Node();
			now = now.go[a[i]];
		}
		now.val += val;
	}
	void build() {
		node = new Node[cnt];
		node[root.id] = root; 
		root.fail = root;
		Queue<Node> que = new LinkedList<Node>();
		for (int i=0; i<m; i++) {
			if (root.go[i] == null)
				root.go[i] = root;
			else {
				root.go[i].fail = root;
				que.add(root.go[i]);
			}
		}
		while (que.size() > 0) {
			Node now = que.poll();
			node[now.id] = now;
			now.val += now.fail.val;
			for (int i=0; i<m; i++) {
				if (now.go[i] == null) {
					now.go[i] = now.fail.go[i];
				} else {
					now.go[i].fail = now.fail.go[i];
					que.add(now.go[i]);
				}
			}
		}
	}
	int getans(int a[], int n, int m, int K) {
		if (a.length == 0) return 0;
		final int mod = 1000000007;
		int len = a.length;
		int[][][][] dp = new int[len+1][cnt][K+1][2];
		for (int i=1; i<a[0]; i++) {
			if (root.go[i].val <= K) {
				dp[1][root.go[i].id][root.go[i].val][0]++;
				if (dp[1][root.go[i].id][root.go[i].val][0] >= mod)
					dp[1][root.go[i].id][root.go[i].val][0] -= mod;
			}
		}
		if (root.go[a[0]].val <= K) {
			dp[1][root.go[a[0]].id][root.go[a[0]].val][1]++;
			if (dp[1][root.go[a[0]].id][root.go[a[0]].val][1] >= mod)
				dp[1][root.go[a[0]].id][root.go[a[0]].val][1] -= mod;
		}
		for (int l=1; l<len; l++) {
			for (int i=1; i<m; i++) {
				if (root.go[i].val <= K) {
					dp[l+1][root.go[i].id][root.go[i].val][0]++;
					if (dp[l+1][root.go[i].id][root.go[i].val][0] >= mod)
						dp[l+1][root.go[i].id][root.go[i].val][0] -= mod;
				}
			}
			for (int c=0; c<cnt; c++) {
				for (int k=0; k<=K; k++) {
					for (int u=0; u<2; u++) {
						if (dp[l][c][k][u] == 0)
							continue;
						for (int i=0; i<m; i++) {
							if (u == 1 && i > a[l])
								break;
							int uto = (u == 1 && i == a[l]) ? 1 : 0;
							int kto = k + node[c].go[i].val;
							int cto = node[c].go[i].id;
							if (kto > K)
								continue;
							dp[l+1][cto][kto][uto] += dp[l][c][k][u];
							if (dp[l+1][cto][kto][uto] >= mod)
								dp[l+1][cto][kto][uto] -= mod;
						}
					}
				}
			}
		}
		int ret = 0;
		for (int c=0; c<cnt; c++) {
			for (int k=0; k<=K; k++) {
				for (int u=0; u<2; u++) {
					ret += dp[len][c][k][u];
					if (ret >= mod)
						ret -= mod;
				}
			}
		}
		return ret;
	}
}

class Reader {
    static BufferedReader reader;
    static StringTokenizer tokenizer;
    static void init(InputStream input) {
        reader = new BufferedReader(new InputStreamReader(input));
        tokenizer = new StringTokenizer("");
    }
    static String next() throws IOException {
        while (!tokenizer.hasMoreTokens()) {
            tokenizer = new StringTokenizer(reader.readLine());
        }
        return tokenizer.nextToken();
    }
    static int nextInt() throws IOException {
        return Integer.parseInt(next());
    }
    static double nextDouble() throws IOException {
        return Double.parseDouble(next());
    }
    static long nextLong() throws IOException {
        return Long.parseLong(next());
    }
    static boolean isend() throws IOException {
    	if (tokenizer.hasMoreTokens())
    		return false;
		do {
			String s = reader.readLine();
			if (s == null)
				return true;
			tokenizer = new StringTokenizer(s);
		} while (!tokenizer.hasMoreTokens());
		return false;
    }
}



  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值