//https://img2018.cnblogs.com/blog/1646268/201908/1646268-20190806114008215-138720377.jpg

2023.7.28解题报告

2023.7.28 DAY5解题报告

T1

考试只会打暴力。

对于第一档分数,\(n,m\le 10\),我们可以直接枚举每一个地方走上还是下,然后直接搜到第 \(n + m - 1\) 个的时候就是到 \((n,m)\) 了,我们直接统计即可。

20pts。

#include <bits/stdc++.h>

#define P 998244353
#define int long long
#define N 310

using namespace std;

int n, m, a[N][N], f[N][N], b[N], ans;

inline void dfs(int x, int y)
{
	if(x == n && y == m)
	{
		b[n + m - 1] = a[n][m];
		for(int i = 1; i <= n + m - 1; i ++)
		{
			int res = 0;
			for(int j = 1; j <= n + m - 1; j ++)
			{
				if(i == j) continue;
				res ^= b[j];
			}
//			cout << "res : " << res << endl;
			ans = (res + ans) % P;
		}
		return ;
	}
	if(x > n || y > m) return ;
	b[x + y - 1] = a[x][y];
	dfs(x + 1, y);
	dfs(x, y + 1);
	return ;
}

signed main()
{
	srand(time(0));
	cin >> n >> m;
	for(int i = 1; i <= n; i ++)
		for(int j = 1; j <= m; j ++)
			cin >> a[i][j];
	if(n <= 50 && m <= 50) dfs(1, 1);
	else ans = (rand() << 4) % P;
	cout << ans << endl;
	return 0;
}

我们设 \(f[i][j][k][0/1]\) 来分别表示,当前在 \(i,j\) 位置,我们异或和是 \(k\) ,当前丢没丢珍珠 \(0/1\) 的方案数。

最后我们的答案就是 \(\sum_{k = 0}^{127} f[i][j][k][1]\times k\)

我们在转移的时候,我们有下面几种情况:

  • 当前点是 \(f[i][j][k][0]\),只能由 \(f[i-1][j][k\oplus a[i][j]][0] + f[i][j-1][k\oplus a[i][j]][0]\) 转移过来。

  • 当前点是 \(f[i][j][k][1]\) 的话,如果是在之前的点就扔掉了,那么就是从 \(f[i-1][j][k\oplus a[i][j]][1] + f[i][j-1][k\oplus a[i][j]][1]\) 加上,还有一种情况就是当前点扔掉,那么就是从 \(f[i-1][j][k][0] + f[i][j-1][k][0]\) 转移过来,求和即可。

然后我们就有了一个近 \(O(n^3)\) 的做法,过这题完全 ok。

#include <bits/stdc++.h>

#define int long long
#define P 998244353
#define N 310

using namespace std;

int n, m, a[N][N], f[N][N][130][2], ans;

signed main()
{
	cin >> n >> m;
	for(int i = 1; i <= n; i ++)
		for(int j = 1; j <= m; j ++)
			cin >> a[i][j];
	f[0][1][0][1] = 1;
	for(int i = 1; i <= n; i ++)
	{
		for(int j = 1; j <= m; j ++)
		{
			if(i == 1 && j == 1)
			{
				f[i][j][a[i][j]][0] = 1;
				f[i][j][0][1] = 1;
				continue;
			}
			for(int k = 0; k < 128; k ++)
			{
				f[i][j][k][0] = (f[i][j][k][0] + f[i - 1][j][k ^ a[i][j]][0]) % P;
				f[i][j][k][0] = (f[i][j][k][0] + f[i][j - 1][k ^ a[i][j]][0]) % P;
				f[i][j][k][1] = (f[i][j][k][1] + f[i - 1][j][k ^ a[i][j]][1]) % P;
				f[i][j][k][1] = (f[i][j][k][1] + f[i][j - 1][k ^ a[i][j]][1]) % P;
				f[i][j][k][1] = (f[i][j][k][1] + f[i - 1][j][k][0]) % P;
				f[i][j][k][1] = (f[i][j][k][1] + f[i][j - 1][k][0]) % P;
			}
		}
	}
	for(int i = 1; i < 128; i ++)
		ans = (ans + f[n][m][i][1] * i) % P;
	cout << ans << endl;
	return 0;
}

T2

暴力:50pts

我们发现里面的 \(n\le 18\),所以我们直接用 DFS 来枚举所有的序列,然后直接 BFS 跑一遍判断是不是符合要求。

我们在 BFS 的时候,我们从枚举的序列第一个元素开始搜,搜的过程中记录哪个点是从哪个店搜过来的,然后最后到了最后一个点就开始判断,如果有没有的,就直接返回不可以。

T2暴力打了俩小时

#include <bits/stdc++.h>

#define P 1000000007
#define int long long
#define N 1000100

using namespace std;

int n, head[N], cnt, stk[N], top, ans, vis[20], b[N], f[N], cnt1;
struct sb{int u, v, next;}e[N];

inline void add(int u, int v) {e[++ cnt] = {u, v, head[u]}; head[u] = cnt;}

inline void dfs2(int s, int len)
{
	int xx = len, ff = 0;
	queue<int> q;
	q.push(s);
	for(int i = 1; i <= n; i ++) vis[i] = f[i] = 0;
	vis[s] = 1;
	while(!q.empty())
	{
		int u = q.front(); q.pop();
		if(u == stk[len]){ ff = 1; break;}	
		for(int i = head[u]; i; i = e[i].next)
		{
			int v = e[i].v;
			if(!vis[v]) vis[v] = 1, f[v] = u, q.push(v);
		}
	}
	if(ff == 0) return ;
	int yy = stk[len];
	while(yy)
	{
		if(yy == stk[xx]) xx --;
		yy = f[yy];
	}
	if(xx == 0) ans ++;
	return ;
}

inline void dfs(int x)
{
	if(x == n + 1)
	{
		top = 0;
		for(int i = 1; i <= n; i ++)
			if(b[i] == 1) stk[++ top] = i;
		if(top < 2) return ;
		dfs2(stk[1], top);
		return ;
	}
	if(x > n) return ;
	dfs(x + 1);
	b[x] = 1;
	dfs(x + 1);
	b[x] = 0;
	return ;
}

signed main()
{
	cin >> n;
	for(int i = 1; i < n; i ++)
	{
		int u, v;
		cin >> u >> v;
		add(u, v);
		add(v, u);
	}
	dfs(1);
	cout << ans << endl;
	return 0;
}

考虑将一条合法的路径在 LCA 处计算进答案。

\(f[i][j]\) 是从当前 \(i\) 子树内以 \(j\) 为结尾的一条祖先大于儿子的链的方案数,\(g[i][j]\) 是从当前 \(i\) 子树内以 \(j\) 为结尾的一条祖先小于儿子的链的方案数。

\(u\) 合并一棵子树 \(v\) 的时候,答案会增加:

\[\sum_{a<b}^{}f_{u,a} \times g_{v,b}+f_{u,b}\times g_{v,a} \]

#include <bits/stdc++.h>

#define int long long
#define P 1000000007
#define N 5010

using namespace std;

int n, f[N][N], g[N][N], ans;
vector<int> to[N];

inline void dfs(int i, int fa)
{
	f[i][i] = g[i][i] = 1;//标记,当前i的子树以i为最后节点,fg都是1
	for(int j : to[i])//遍历里面的元素,所有的终点
	{
		if (j == fa) continue ;//如果是父节点就跳过
		dfs(j, i);//继续搜索
		int s1 = 0, s2 = 0;//统计
		for(int k = 1; k <= n; k ++)//遍历所有的点
		{
			ans = (ans + 1ll * s1 * g[j][k] + 1ll * s2 * g[i][k]) % P;//把所有的子树到k的方案撑起来
			s1 = (s1 + f[i][k]) % P;//累加上到i的方案数
			s2 = (s2 + f[j][k]) % P;//累加方案数
		}
		for(int k = 1; k < i; k ++)
			f[i][i] = (f[i][i] + f[j][k]) % P;//把所有的儿子节点都合并到当前点
		for(int k = n; k > i; k --)
			g[i][i] = (g[i][i] + g[j][k]) % P;
		for(int k = 1; k <= n; k ++)
		{
			f[i][k] = (f[i][k] + f[j][k]) % P;//累加
			g[i][k] = (g[i][k] + g[j][k]) % P;//累加
		}
	}
	return ;
}

signed main()
{
	ios::sync_with_stdio(0), cin.tie(0);
	cin >> n;
	for(int i = 1; i < n; i ++)
	{
		int x, y;
		cin >> x >> y;
		to[x].push_back(y);//存图
		to[y].push_back(x);
	}
	dfs(1, 0);//深搜
	cout << ans << endl;
	return 0;
}

T3

暴力:42pts

我们枚举 \(n\) 个元素,然后我们判断最后和是不是等于 \(m\) 然后判断即可,我们可以一旦大于 \(m\) 就退出等来优化 DFS,但没用,还是 42pts。

#include <bits/stdc++.h>

#define P 1000000009
#define int long long
#define N 1000100

using namespace std;

int n, m, ans, b[N];

inline void dfs(int x, int sum)
{
	if(x == n + 1 && sum == m)
	{
		int res = 0;
		for(int i = 1; i <= n; i ++)
			res ^= b[i];
		ans = (ans + res) % P;
		return ;
	}
	if(x > n || sum > m) return ;
	for(int i = 0; i <= m; i ++)
	{
		b[x] = i;
		dfs(x + 1, sum + b[x]);
	}
	return ;
}

signed main()
{
	cin >> n >> m;
	dfs(1, 0);
	cout << ans << endl;
	return 0;
}

正解:

std:

#include<bits/stdc++.h>

#define P 1000000009
#define int long long
#define N 3010
#define M 40

using namespace std;

int n, m, ans, f[M][N], g[M][N], C[N][N];

inline void init()
{
	for(int i = 0; n >= i; i ++) C[i][0] = 1;//递推预处理组合数
	for(int i = 1; n >= i; i ++)
		for(int j = 1; n >= j; j ++)
			C[i][j] = (C[i - 1][j] + C[i - 1][j - 1]) % P;
	return ;
}
inline int add(int x, int y) {return ((x % P) + (y % P)) % P;}//加法取模
inline int mul(int x, int y) {return ((x % P) * (y % P)) % P;}//乘法取模

signed main()
{
	cin >> n >> m;
	init();
	f[0][0] = 1;//
	for(int i = 0; i <= 30; i ++)
		for(int j = 0; j <= n; j ++)
			for(int k = 0; k <= n; k ++)
				if(((j + k) & 1) == ((m >> i) & 1))
				{
					f[i + 1][(j + k) / 2] = add(f[i + 1][(j + k) / 2], mul(f[i][j], C[n][k]));
					g[i + 1][(j + k) / 2] = add(g[i + 1][(j + k) / 2], add(mul(g[i][j], C[n][k]), mul(mul(((k & 1) << i), f[i][j]), C[n][k])));
				}
	cout << g[31][0] << endl;
	return 0;
} 

T4

暴力:45pts

我们发现 \(n \le 10\),但是 \(m \le 3e5\),所以肯定有很多区间是重复的,我们开个map来记录一下。

一开始我是打算套个pair来解决的,但是发现好像不太行,所以我就改成 HASH 了(其实也不算吧)用行乘上一个质数,然后加上列来判断是哪一行哪一列。

我们直接枚举所有的店花多少钱来装修,也就是差不多 \(4^{10}\) 的方案数。

然后我们循环判断每一个区间的最大获利,先直接枚举区间的左右端点,这样最多是 \(100\) 种方案,比题目里的 \(m\) 小的多。

我们最后要减去装修的花费。

memset 真慢改成循环就过大样例了我还以为我做法假了

#include <bits/stdc++.h>

#define int long long
#define N 1000100
#define M 110

using namespace std;

inline int read(){int x=0,f=1;char ch=getchar();while(!isdigit(ch)){f=ch!='-';ch=getchar();}while(isdigit(ch)){x=(x<<1)+(x<<3)+(ch^48);ch=getchar();}return f?x:-x;}

map<int, int> mp;
int n, m, k, c[M][M], ans = -1e18, b[M];
int len;
struct sb{int l, r;}e[N];

inline int cmp(sb a, sb b)
{
	if(a.l == b.l)  return a.r < b.r;
	else return a.l < b.l;
}

inline void dfs(int x)
{
	if(x == n + 1)
	{
		int res = 0;
		for(int l = 1; l <= n; l ++)
		{
			for(int r = l; r <= n; r ++)
			{
				int maxn = 0;
				for(int j = l; j <= r; j ++)
					maxn = max(maxn, b[j]);
				res += maxn * mp[(l * 131 + r)];
			}
		}
		for(int i = 1; i <= n; i ++)
			res -= c[i][b[i]];
		ans = max(ans, res);
		return ;
	}
	if(x > n) return ;
	for(int i = 0; i <= k; i ++)
	{
		b[x] = i;
		dfs(x + 1);
	}
	return ;
}

signed main()
{
//	freopen("c2.in", "r", stdin);
	n = read(), m = read(), k = read();
	for(int i = 1; i <= n; i ++)
		for(int j = 1; j <= k; j ++)
			c[i][j] = read();
	for(int i = 1; i <= m; i ++)
		e[i] = (sb){read(), read()};
	sort(e + 1, e + m + 1, cmp);
	for(int i = 1; i <= m; i ++)
		mp[(e[i].l * 131 + e[i].r)] ++;
	dfs(1);
	cout << ans << endl;
	return 0;
}

.

posted @ 2023-07-28 22:22  北烛青澜  阅读(7)  评论(0)    收藏  举报