1 条题解
-
0
自动搬运
来自洛谷,原作者为

Alex_Wei
**搬运于
2025-08-24 22:54:10,当前版本为作者最后更新于2024-01-16 18:46:30,作者可能在搬运后再次修改,您可在原文处查看最新版自动搬运只会搬运当前题目点赞数最高的题解,您可前往洛谷题解查看更多
以下是正文
P10047 [CCPC 2023 北京市赛] 勿蹖宠物
对每个回文串,考虑它如何被统计入答案。如果只是单侧添加单词,则 DP 过程中难以满足回文的要求。考虑往两侧添加单词,哪一侧长度更短就往哪一侧加入单词,长度相同则规定往左边加入单词。
为了满足回文的限制,记录必要信息,设计 DP 表示当前状态是左侧更长还是右侧更长,较短的一侧长度为 ,较长的一侧(从边界开始数)第 个字符是第 个字符串的第 个字符,基于这些信息可以确定回文串已经匹配了左右两侧多少个字符,以及除去左右两侧匹配的 个字符之后剩余部分的具体形态。注意 状态总数为 。
转移即枚举要放的一侧放哪个字符串,判定合法性需要预处理 在 中的所有出现位置,以及 的每个前缀和 对应长度的后缀是否相等。时间复杂度 。
细节太多不想写怎么办?考虑从外往里依次确定每个字符,需要记录匹配长度,以及左右两侧分别匹配到哪个字符串的第几个字符。若某一侧刚好匹配完,则这一侧需要枚举新的字符串。时间复杂度 ,但代码非常好写。
#include <bits/stdc++.h> using namespace std; using ll = long long; using pii = pair<int, int>; using pll = pair<ll, ll>; using pdi = pair<double, int>; using pdd = pair<double, double>; using ull = unsigned long long; using LL = __int128_t; #define ppc(x) __builtin_popcount(x) #define clz(x) __builtin_clz(x) bool Mbe; constexpr int mod = 1e9 + 7; void addt(int &x, int y) { x += y, x >= mod && (x -= mod); } int add(int x, int y) { return x += y, x >= mod && (x -= mod), x; } // ---------- templates above ---------- constexpr int N = 600 + 5; pii pos[N], vk[N], vl[N]; int n, L, cnt, lb[N][N]; int f[N][N], g[N][N]; string s[N]; void solve() { cin >> n >> L; for(int i = 1; i <= n; i++) { cin >> s[i]; for(int j = 0; j + 1 < s[i].size(); j++) { pos[++cnt] = {i, j}; lb[i][j] = cnt; } } f[0][0] = 1; for(int p = 0; p < L / 2; p++) { memset(g, 0, sizeof(g)); int tot = 0; for(int i = 0; i <= cnt; i++) { for(int j = 0; j <= cnt; j++) { if(!f[i][j]) continue; int pk = 1, pl = 1; if(i) vk[0] = {pos[i].first, pos[i].second + 1}; else { pk = n; for(int p = 1; p <= n; p++) vk[p - 1] = {p, 0}; } if(j) vl[0] = {pos[j].first, pos[j].second - 1}; else { pl = n; for(int p = 1; p <= n; p++) vl[p - 1] = {p, int(s[p].size()) - 2}; } tot += pk * pl; for(int _k = 0; _k < pk; _k++) { for(int _l = 0; _l < pl; _l++) { pii k = vk[_k], l = vl[_l]; int idk = k.first, pk = k.second, sk = lb[idk][pk]; int idl = l.first, pl = l.second + 1, sl = pl ? lb[idl][pl - 1] : 0; if(s[idk][pk] == s[idl][pl]) addt(g[sk][sl], f[i][j]); } } } } swap(f, g); } int ans = 0; if(L & 1 ^ 1) { for(int i = 0; i <= cnt; i++) addt(ans, f[i][i]); } else { for(int i = 1; i <= n; i++) { if(s[i].size() == 1) addt(ans, f[0][0]); else { addt(ans, f[lb[i][s[i].size() - 2]][0]); addt(ans, f[0][lb[i][0]]); for(int j = 1; j < s[i].size() - 1; j++) { addt(ans, f[lb[i][j - 1]][lb[i][j]]); } } } } cout << ans << "\n"; } bool Med; signed main() { fprintf(stderr, "%.3lf MB\n", (&Mbe - &Med) / 1048576.0); // ios::sync_with_stdio(0), cin.tie(0), cout.tie(0); #ifdef ALEX_WEI FILE* IN = freopen("1.in", "r", stdin); FILE* OUT = freopen("1.out", "w", stdout); #endif int T = 1; while(T--) solve(); fprintf(stderr, "%.3lf ms\n", 1e3 * clock() / CLOCKS_PER_SEC); return 0; } /* g++ a.cpp -o a -std=c++14 -O2 -DALEX_WEI */
- 1
信息
- ID
- 9664
- 时间
- 1500ms
- 内存
- 1024MiB
- 难度
- 6
- 标签
- 递交数
- 0
- 已通过
- 0
- 上传者