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\) 的时候,答案会增加:
#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;
}
.
本文来自博客园,作者:北烛青澜,转载请注明原文链接:https://www.cnblogs.com/Multitree/articles/17589039.html
The heart is higher than the sky, and life is thinner than paper.
浙公网安备 33010602011771号