每日构造/DP(4.25)
听说很多人赛时都被这道题卡了。。感觉并不难想啊。。
逆向思考,原题意相当于如果存在 \(b[i] = b[i-1]\) ,则 \(b[i]\) 可以放在前方任意一个位置,然后这题就做完了。。
具体做法的话先统计 \(b\) 数组中哪些数可以放前排去,然后开双指针,如果 \(l\) 与 \(r\) 指向的当前位匹配不上,就把刚刚统计的数中与 \(a[l]\) 相匹配且位置靠前的数放在 \(r\) 指向的位置,可以证明这样的贪心是正确的。。然后如果出现某个位置既不匹配又没数可放就寄了。。
#include <bits/stdc++.h>
#define IOS \
std::ios::sync_with_stdio(false); \
std::cin.tie(0); \
std::cout.tie(0);
bool solve()
{
int n;
std::cin >> n;
std::vector<int> a(n + 1), b(n + 1), vis(n + 1);
for (int i = 1; i <= n; ++i)
std::cin >> a[i];
for (int i = 1; i <= n; ++i)
std::cin >> b[i];
std::queue<int> q[n + 1];
for (int i = 1; i <= n; ++i)
if (b[i] == b[i - 1])
q[b[i]].push(i);
int l = 1, r = 1, delta = 0; // delta记录从后面拿了多少个数放前面,每放一个,后面的数相当于往后挪了一位
while (l <= n && r <= n)
{
while (vis[r])
++r;
if (a[l] == b[r])
vis[r] = 1, ++l, ++r;
else
{
if (q[a[l]].empty())
return false;
int now = q[a[l]].front();
while (vis[now])
{
q[a[l]].pop();
if (q[a[l]].empty())
return false;
now = q[a[l]].front();
}
if (now + delta > l)
{
vis[now] = 1;
++l, ++delta;
}
else
return false;
}
}
return true;
}
int main()
{
IOS;
int t;
std::cin >> t;
while (t--)
std::cout << (solve() ? "YES" : "NO") << std::endl;
return 0;
}
设 \(f[i][j]\) 表示 \(|S| = i,|T| = j\) 的方案数,有转移方程:
\(f[i+k][j+c(k)+1] += 25*f[i][j]\) , \(c(k)\) 表示数字 \(k\) 的位数
复杂度为 \(O(n^3)\) ,考虑优化,由于 \(f[i][j]\) 对相同位数的 \(k\) 作的贡献是相同的,因此想到区间加
区间加可以用什么实现呢?差分、前缀和、树状数组等等均可。这里用的是差分, \(d[i][j] = f[i][j] - f[i-1][j]\)
#include <bits/stdc++.h>
#define IOS \
std::ios::sync_with_stdio(false); \
std::cin.tie(0); \
std::cout.tie(0);
using ll = long long;
int main()
{
IOS;
int n, p;
std::cin >> n >> p;
std::vector<std::vector<ll>> f(n + 1, std::vector<ll>(n + 1));
std::vector<std::vector<ll>> d(n + 2, std::vector<ll>(n + 1));
auto calc = [&](int x)
{
int res = 1;
while (x)
x /= 10, ++res;
return res;
};
std::vector<int> ten = {0, 1, 10, 100, 1000, 10000};
std::vector<int> len(n + 1);
for (int i = 1; i <= n; ++i)
len[i] = calc(i);
for (int i = 1; i <= n; ++i)
d[i][len[i]] += 26, d[i + 1][len[i]] -= 26;
for (int i = 1; i <= n; ++i)
{
for (int j = len[i]; j <= n; ++j)
{
f[i][j] = (d[i][j] + f[i - 1][j]) % p;
for (int k = 1; i + ten[k] <= n && j + k + 1 <= n; ++k)
{
d[i + ten[k]][j + k + 1] = (d[i + ten[k]][j + k + 1] + 25 * f[i][j]) % p;
if (i + ten[k + 1] <= n)
d[i + ten[k + 1]][j + k + 1] = ((d[i + ten[k + 1]][j + k + 1] - 25 * f[i][j]) % p + p) % p;
}
}
}
ll ans = 0;
for (int i = 1; i < n; ++i)
ans = (ans + f[n][i]) % p;
std::cout << ans;
return 0;
}