[HZOI] CSP-S模拟37 赛后总结
[HZOI] CSP-S模拟37 赛后总结
我就是大 DP fw!
今天是 dp 专场。
爆炸心路历程
直接开 T1。
据说是签到题。。。但是我没签上到(心态炸了qwq)
题面描述很简单,就是拼好串,求最大回文串长度。
赛时开题一看回文串!
坏了,忘了马拉车怎么打了,有点心慌。
然后直接去考虑怎么贪心去了。
想了一万个贪心策略,全被自己 hack 了,然后就这样,2 hours 过去了。
开始慌到爆炸!
要保龄了要保龄了,怎么办怎么办!?
我不想保龄啊啊啊
然后发现貌似可以dp。
然后设了个特别离谱的四维dp。
开始狂推不止,推不出来。。。
大脑根本无法思考,什么!?已经过去 3 hours了。
不行,先开后边的题吧,至少拿个暴力分。
欸?T2怎么这么简单?切了切了
欸?T3暴力能有 70 tps!?打了打了
欸?T4我会打 30 tps!做了做了
然后在半个小时的时间内拿了所有的分。。。
之后最后半小时继续凹 T1,要不先打个暴力吧,能有 20 tps先。
最后 T1 暴力打假了。。。
T4 调试语句忘删了,挂了 30。。。
最后得分:[0 + 100 + 70 + 0] --> [170];
rk16。
打的真垃圾。。。
快考试了打模拟赛能不能上点心。。。
模拟赛能不能再爱我一次♪♪♪
T1:回文
直接考虑区间 dp。
设 dp 状态为: \(dp_{la,ra,lb,rb}\) 代表 \(a\) 串中 \([la,ra]\) 与 \(b\) 串中 \([lb,rb]\) 是否可以拼成回文串。
然后大力分讨出转移方程。
注意初始化 \(len\) 为 \(1\) 和 \(len\) 为 \(2\) 都要赋初值(因为回文串分奇偶)。
最后放代码
点击查看代码
#include<bits/stdc++.h>
#define Blue_Archive return 0
using namespace std;
constexpr int N = 53;
int T;
int n;
int m;
int ans;
bool dp[N][N][N][N];
string a,b;
inline int read()
{
int x = 0,op = 1;
char c = getchar_unlocked();
while(c < '0' || c > '9')
{
if(c == '-') op = -1;
c = getchar_unlocked();
}
while(c <= '9' && c >= '0') x = (x << 1) + (x << 3) + (c ^ 48),c = getchar_unlocked();
return op * x;
}
inline void write(int x)
{
if(x < 0) x = -x,putchar_unlocked('-');
if(x > 9) write(x / 10);
putchar_unlocked(x % 10 + '0');
}
inline void work()
{
ans = 1;
memset(dp,0,sizeof(dp));
cin >> a >> b;
n = a.length();
m = b.length();
a = ' ' + a;
b = ' ' + b;
a[0] = '$';
b[0] = '%';
a[n + 1] = '&';
b[m + 1] = '^';
for(int i = 1;i <= n + 1;i ++)
{
for(int j = 1;j <= m + 1;j ++)
{
dp[i][i][j][j] = (a[i] == b[j]);
dp[i][i - 1][j][j - 1] = 1;
dp[i][i - 1][j][j] = 1;
dp[i][i][j][j - 1] = 1;
if(a[i] == a[i - 1]) dp[i - 1][i][j][j - 1] = 1;
if(b[j] == b[j - 1]) dp[i][i - 1][j - 1][j] = 1;
}
}
for(int la = 1;la <= n + 1;la ++)
{
for(int ra = la - 1;ra <= n;ra ++)
{
for(int lb = 1;lb <= m + 1;lb ++)
{
for(int rb = lb - 1;rb <= m;rb ++)
{
dp[la][ra][lb][rb] |=
(dp[la + 1][ra - 1][lb][rb] & (a[la] == a[ra])) |
(dp[la][ra][lb + 1][rb - 1] & (b[lb] == b[rb])) |
(dp[la + 1][ra][lb][rb - 1] & (a[la] == b[rb])) |
(dp[la][ra - 1][lb + 1][rb] & (a[ra] == b[lb])) |
(dp[la + 1][ra - 1][lb + 1][rb - 1] & (a[la] == a[ra]) & (b[lb] == b[rb]));
if(dp[la][ra][lb][rb]) ans = max(ans,ra - la + rb - lb + 2);
}
}
}
}
for(int la = n + 1;la >= 1;la --)
{
for(int ra = la - 1;ra <= n;ra ++)
{
for(int lb = m + 1;lb >= 1;lb --)
{
for(int rb = lb - 1;rb <= m;rb ++)
{
dp[la][ra][lb][rb] |=
(dp[la + 1][ra - 1][lb][rb] & (a[la] == a[ra])) |
(dp[la][ra][lb + 1][rb - 1] & (b[lb] == b[rb])) |
(dp[la + 1][ra][lb][rb - 1] & (a[la] == b[rb])) |
(dp[la][ra - 1][lb + 1][rb] & (a[ra] == b[lb])) |
(dp[la + 1][ra - 1][lb + 1][rb - 1] & (a[la] == a[ra]) & (b[lb] == b[rb]));
if(dp[la][ra][lb][rb]) ans = max(ans,ra - la + rb - lb + 2);
}
}
}
}
cout << ans << '\n';
}
signed main()
{
freopen("string.in","r",stdin);freopen("string.out","w",stdout);
// freopen("data.in","r",stdin);freopen("data.out","w",stdout);
ios::sync_with_stdio(0);cin.tie(0);cout.tie(0);
cin >> T;
while(T --) work();
Blue_Archive;
}
T2:数排列
大简单题,直接考虑 dp 即可。
dp 状态为: \(dp_{i,j}\) 第 \(i\) 个数放在第 \(i\) 个序列中的第 \(j\) 位的方案数。
朴素转移有 \(O(n^3)\)。
前缀和优化即可做到 \(O(n^2)\)。
点击查看代码
#include<bits/stdc++.h>
#define int long long
#define Blue_Archive return 0
using namespace std;
constexpr int N = 2e3 + 3;
constexpr int mod = 1e9 + 7;
int n;
int ans;
int dp[N][N];
int sum[N][N];
char a[N];
signed main()
{
// freopen("data.in","r",stdin);freopen("data.out","w",stdout);
freopen("perm.in","r",stdin);freopen("perm.out","w",stdout);
ios::sync_with_stdio(0);cin.tie(0);cout.tie(0);
cin >> n;
for(int i = 1;i < n;i ++) cin >> a[i];
dp[1][1] = 1;
sum[1][1] = 1;
for(int i = 2;i <= n;i ++)
{
for(int j = 1;j <= i;j ++)
{
if(a[i - 1] == '1') dp[i][j] = sum[i - 1][j - 1];
else dp[i][j] = ((sum[i - 1][i - 1] - sum[i - 1][j - 1]) % mod + mod) % mod;
sum[i][j] = (sum[i][j - 1] + dp[i][j]) % mod;
}
}
cout << sum[n][n] << '\n';
Blue_Archive;
}
T3:树上的背包
神秘折半搜。
考虑01背包的定义:是否选择每一个物品。
考虑是棵完全二叉树,每个点最多可以选 \(log_2n\) 个物品。
直接折半搜索,时间复杂度是 \(O(q2^\frac{log_2n}{2})\) 也就是 \(O(q \sqrt{n})\) 可以通过此题。
什么暴力比正解难打。。。
点击查看代码
#include<bits/stdc++.h>
#define con putchar_unlocked(' ')
#define ent putchar_unlocked('\n')
#define lowbit(x) (x & (-x))
#define Blue_Archive return 0
using namespace std;
constexpr int N = 1e5 + 3;
constexpr int INF = 1e9 + 3;
int n;
int q;
int V;
int ans;
int top;
int tr[N];
int stk[301];
struct miku
{
int w,val;
}a[N];
inline int read()
{
int x = 0,op = 1;
char c = getchar_unlocked();
while(c < '0' || c > '9')
{
if(c == '-') op = -1;
c = getchar_unlocked();
}
while(c <= '9' && c >= '0') x = (x << 1) + (x << 3) + (c ^ 48),c = getchar_unlocked();
return op * x;
}
inline void write(int x)
{
if(x < 0) x = -x,putchar_unlocked('-');
if(x > 9) write(x / 10);
putchar_unlocked(x % 10 + '0');
}
inline void update(int pos,int val){for(int i = pos;i <= V + 1;i += lowbit(i)) tr[i] = max(tr[i],val);}
inline int query(int pos){int res = -INF;for(int i = pos;i;i -= lowbit(i)) res = max(res,tr[i]);return res;}
inline void dfs1(int x,int up,int sum,int res)
{
if(sum > V) return;
if(x == up + 1)
{
update(sum + 1,res);
return;
}
dfs1(x + 1,up,sum,res);
dfs1(x + 1,up,sum + a[stk[x]].w,res + a[stk[x]].val);
}
inline void dfs2(int x,int up,int sum,int res)
{
if(sum > V) return;
if(x == up + 1)
{
ans = max(ans,query(V - sum + 1) + res);
return;
}
dfs2(x + 1,up,sum,res);
dfs2(x + 1,up,sum + a[stk[x]].w,res + a[stk[x]].val);
}
signed main()
{
freopen("knapsack.in","r",stdin);freopen("knapsack.out","w",stdout);
// freopen("data.in","r",stdin);freopen("data.out","w",stdout);
n = read();
for(int i = 1;i <= n;i ++)
{
a[i].val = read();
a[i].w = read();
}
q = read();
for(int i = 1,x;i <= q;i ++)
{
for(int j = 1;j <= V + 1;j ++) tr[j] = 0;
ans = 0;
x = read();
V = read();
top = 0;
while(x) stk[++ top] = x,x /= 2;
dfs1(1,top / 2,0,0);
dfs2(top / 2 + 1,top,0,0);
write(ans),ent;
}
Blue_Archive;
}
T4:树上的背包
不会,咕咕咕。

浙公网安备 33010602011771号