A
B

[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:树上的背包

不会,咕咕咕。

posted @ 2025-10-24 19:31  MyShiroko  阅读(25)  评论(1)    收藏  举报