CSP-S 38
10.24
连续inf场没有切掉 t1 了\(\ldots\)
0+100+70+30=200
甚至 t1 是唯一爆蛋的。
2h 写 t1 获得 0pts 好成绩,剩下 2h 获得 200pts 。
难崩。
t1
第一眼:好像那道\(O(n^3)\) dp 。
第二眼:坏了回文串咋转移?
结果就是 2h 打了坨大的。
正解为四维dp,时间复杂度\(O(n^4)\)
首先要发现性质:
一定是 \(a\) , \(b\) 两串中连续的两部分穿插拼接更优。
考虑若不连续,则显然可以将其扔到最前(后)面,找到更优(起码不劣)解。
于是就有了四维可行性dp。
设 \(dp_{i,j,l,r}\) 表示 \(a\) 串 \(i\) 到 \(j\) 与 \(b\) 串 \(l\) 到 \(r\) 能否拼成回文串(0/1)。
枚举 \(i,j,l,r\) 即可(或改为枚举长度)。
转移是朴素的,若 \(a\) 串向左右扩展可行则转移,剩下三种情况同理。
注意初始化一定要到位(奇偶回文,上界问题)。
详见code
代码
#include <bits/stdc++.h>
using namespace std;
int T;
char s[55], t[55];
bool dp[60][60][60][60]; // 0/1 表示能否构成回文
signed main()
{
freopen("string.in", "r", stdin);
freopen("string.out", "w", stdout);
ios::sync_with_stdio(0);
cin.tie(0);
cin >> T;
while (T--)
{
cin >> (s + 1) >> (t + 1);
int len1 = strlen(s + 1), len2 = strlen(t + 1);
s[0] = '%', t[0] = '&', s[len1 + 1] = '#', t[len2 + 1] = '$';
memset(dp, 0, sizeof(dp));
int ans = 1;
// 初始化
for (int i = 1; i <= len1; ++i)
for (int j = 1; j <= len2 + 1; ++j)
dp[i][i][j][j - 1] = 1;
for (int i = 1; i <= len2; ++i)
for (int j = 1; j <= len1 + 1; ++j)
dp[j][j - 1][i][i] = 1;
for (int i = 1; i <= len1; ++i)
for (int j = 1; j <= len2; ++j)
{
if (s[i] == t[j])
dp[i][i][j][j] = 1;
ans = 2;
}
for (int i = 1; i < len1; ++i)
if (s[i] == s[i + 1])
{
for (int j = 1; j <= len2 + 1; ++j)
dp[i][i + 1][j][j - 1] = 1;
ans = 2;
}
for (int i = 1; i < len2; ++i)
if (t[i] == t[i + 1])
{
for (int j = 1; j <= len1 + 1; ++j)
dp[j][j - 1][i][i + 1] = 1;
ans = 2;
}
// 可行性dp
for (int la = 0; la <= len1; ++la)
{
for (int i = 1; i <= len1 - la + 1; ++i)
{
int j = i + la - 1;
for (int lb = 0; lb <= len2; ++lb)
{
for (int p = 1; p <= len2 - lb + 1; ++p)
{
int q = p + lb - 1;
if (dp[i][j][p][q])
{
ans = max(ans, la + lb);
if (i > 1 && j < len1 && s[i - 1] == s[j + 1])
dp[i - 1][j + 1][p][q] = 1;
if (i > 1 && q < len2 && s[i - 1] == t[q + 1])
dp[i - 1][j][p][q + 1] = 1;
if (p > 1 && j < len1 && t[p - 1] == s[j + 1])
dp[i][j + 1][p - 1][q] = 1;
if (p > 1 && q < len2 && t[p - 1] == t[q + 1])
dp[i][j][p - 1][q + 1] = 1;
}
}
}
}
}
cout << ans << "\n";
}
return 0;
}
t2
我去怎么是计数,完了要爆了wwwww
诶?这个好像简单。
切了。
第一道独立完成的计数题。
这题真的简单于 t1 吧?
容易发现,对于每个 \(i\) ,方案数只与 \(i-1\) 在每一位置上的出现次数有关,维护这个就行。
具体的,设共 \(n\) 个数,若 \(a_{i-1}=1\),答案为:
\(\sum\limits_{i=1}^{n-1}f_i\times(i-j)\)
其中\(f_i\) 本为 \(f_{i,j}\) 表示数 \(i\) 在位置 \(j\) 上出现的次数,由于每次转移只与 \(f_{i-1,j}\) 有关,所以滚掉第一维。
对于 \(a_{i-1}=0\) 的情况类似,请自行推导。
\(f\) 的转移为一段前缀(或后缀)的累加(0/1情况不同),前缀和优化即可。
最终时间复杂度\(O(n^2)\)
这道思路,转移和优化都不难,多模数据就出了。
所以为啥这道不是 t1 ?
code
哒哒
#include <bits/stdc++.h>
#define int long long
using namespace std;
const int N = 2010;
const int mod = 1e9 + 7;
int n, ans;
int a[N], f[N], g[N];
signed main()
{
freopen("perm.in", "r", stdin);
freopen("perm.out", "w", stdout);
ios::sync_with_stdio(0);
cin.tie(0);
cin >> n;
for (int i = 1; i < n; ++i)
cin >> a[i];
f[1] = 1;
for (int i = 2; i <= n; ++i)
{
ans = 0;
if (a[i - 1]) // 1 i < i+1
{
for (int j = 1; j < i; ++j)
{
(ans += f[j] * (i - j) % mod) %= mod;
g[j + 1] = f[j];
}
for (int j = 1; j <= i; ++j)
g[j] = (g[j - 1] + g[j]) % mod;
}
else // 0 i > i+1
{
for (int j = i; j; --j)
{
(ans += f[j] * j % mod) %= mod;
g[j] = f[j];
}
for (int j = i - 1; j; --j)
g[j] = (g[j + 1] + g[j]) % mod;
}
memcpy(f, g, sizeof(g));
memset(g, 0, sizeof(g));
}
cout << ans << "\n";
return 0;
}
// 272
// 938843096
t3
暴力魅力时刻。
我去暴力竟然有70pts!
暴力了。
正解:
发现每次点数不会超过 \(\log_2n\)
对于小于 400 的部分直接dp即可。
(\(100000/(2^8)\approx 400\))
大于400的部分暴搜,之后拼接答案即可。
其实就是根号分治(应该是吧,反正大体一致)。
code:
暴力!暴力!暴力!
#include <bits/stdc++.h>
#define pir pair<int, int>
#define fi first // val
#define se second // weight
using namespace std;
const int N = 1e5 + 10;
int n, q;
pir a[N], d[N];
int num[20], tot, dp[401][N];
int cnt;
inline void dfs(int id, int sum, int lim, int up)
{
if (lim > up)
return;
if (id > tot)
{
d[++cnt] = {sum, lim};
return;
}
dfs(id + 1, sum + a[num[id]].fi, lim + a[num[id]].se, up);
dfs(id + 1, sum, lim, up);
}
signed main()
{
freopen("knapsack.in", "r", stdin);
freopen("knapsack.out", "w", stdout);
ios::sync_with_stdio(0);
cin.tie(0);
cin >> n;
for (int i = 1; i <= n; ++i)
cin >> a[i].fi >> a[i].se;
for (int i = 1; i <= 400; ++i)
{
memcpy(dp[i], dp[i >> 1], sizeof(dp[i]));
for (int j = 100000; j >= a[i].se; --j)
dp[i][j] = max(dp[i][j], dp[i][j - a[i].se] + a[i].fi);
}
cin >> q;
int x, up;
while (q--)
{
cin >> x >> up;
if (x <= 400)
{
cout << dp[x][up] << "\n";
continue;
}
tot = 0, cnt = 0;
while (x > 400)
{
num[++tot] = x;
x >>= 1;
}
int ans = 0;
dfs(1, 0, 0, up);
for (int i = 1; i <= cnt; ++i)
ans = max(ans, d[i].fi + dp[x][up - d[i].se]);
cout << ans << "\n";
}
return 0;
}
t4
咕咕咕
本文来自博客园,作者:HS_fu3,转载请注明原文链接:https://www.cnblogs.com/HS-fu3/p/19164004

浙公网安备 33010602011771号