2023.7.31 DAY8 解题报告
2023.7.31 DAY8 解题报告
T1
考试想了一个做法,但是 80 分,考完试想起来有个地方处理的不对,处理完就 A 了。
我们观察一下发现,如果一条链没到头肯定是不优的,因为异或最小就是 0,我们加上也没影响,所以我们可以直接按链上所有都选来算。
我们考虑维护两个值,以当前点的子节点往下的链最大的值以及次大值。
我们这个时候发现,一个点的答案就是最大值与次大值之和。
我们在搜完之后直接 \(O(n)\) 遍历一遍就好。
挂了 20.
#include <bits/stdc++.h>
#define int long long
#define N 1000100
using namespace std;
//0是最大,1是次大
int n, m, a[N], maxn[N][2], f[N], cnt, head[N], ans;
struct sb{int u, v, next;}e[N];
inline void add(int u, int v)
{
e[++ cnt] = (sb){u, v, head[u]};
head[u] = cnt;
return ;
}
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;}
inline void dfs(int x)
{
for(int i = head[x]; i; i = e[i].next)
{
int v = e[i].v;
dfs(v);
int val = a[v] ^ a[x];
if(maxn[x][0] < maxn[v][0] + val)
{
maxn[x][1] = max(maxn[x][0], maxn[x][1]);//少这句
maxn[x][0] = maxn[v][0] + val;
}
else if(maxn[x][1] < maxn[v][0] + val) maxn[x][1] = maxn[v][0] + val;
else if(maxn[x][1] < maxn[v][1] + val) maxn[x][1] = maxn[v][1] + val;
else continue;
}
return ;
}
signed main()
{
// freopen("a_1.in", "r", stdin);
n = read();
for(int i = 1; i <= n; i ++)
a[i] = read();
for(int i = 2; i <= n; i ++)
f[i] = read(), add(f[i], i);
dfs(1);
for(int i = 1; i <= n; i ++)
ans = max(ans, maxn[i][0] + maxn[i][1]);
cout << ans << endl;
return 0;
}
T2
我们考虑一下如何搜索,既然都说了是 DAG,那么我直接每一个点都DFS一次,把到的点加一,如果是奇数就直接答案加1,偶数就答案减一。
20分。
#include <bits/stdc++.h>
#define umap unordered_map
#define int long long
#define N 1000100
using namespace std;
const int base = 50021;
int head[N], cnt, n, m, ans;
struct sb{int u, v, next;}e[N << 1];
umap<int, int> mp;
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;}
inline void add(int u, int v)
{
e[++ cnt] = (sb){u, v, head[u]};
head[u] = cnt;
return ;
}
inline void dfs(int x, int tp)
{
for(int i = head[x]; i; i = e[i].next)
{
int v = e[i].v;
int tmp = tp * base + v;
mp[tmp] ++;
if(mp[tmp] & 1) ans ++;
else ans --;
dfs(v, tp);
}
return ;
}
signed main()
{
// freopen("b_1.in", "r", stdin);
n = read(), m = read();
for(int i = 1; i <= m; i ++)
add(read(), read());
for(int i = 1; i <= n; i ++)
dfs(i, i);
cout << ans << endl;
return 0;
}
我们发现二维数组难以存放我们的路径数,但是答案只与奇偶性有关,所以可以用 bitset 和异或来存储。
但是时间不够,我们发现可以利用之前存的一个点的情况来更新能到他的点的情况。
#include <bits/stdc++.h>
//#define umap unordered_map
#define P 1000000007
#define int long long
#define N 100010
using namespace std;
//umap<int, int>;
int n, m, fa[N], f[N][110], b[N], ans, ff, head[N], cnt, vis[N], V[N];
struct sb{int u, v, next;}e[N];
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;}
inline int stling(int p, int k)
{
f[0][1] = 1;
for(int i = 1; i <= p; i ++)
for(int j = 1; j <= min(i, k); j ++)
f[i][j] = (f[i - 1][j] * j + f[i - 1][j - 1]) % P;
return f[p][k];
}
inline void add(int u, int v)
{
e[++ cnt] = (sb){u, v, head[u]};
head[u] = cnt;
return ;
}
inline int pd()
{
for(int i = 0; i < m; i ++) V[i] = 0;
for(int i = 1; i <= n; i ++) V[b[i]] = 1;
for(int i = 0; i < m; i ++) if(V[i] == 0) return 0;
queue<int> q;
for(int i = 1; i <= n; i ++) vis[i] = 0;
int s = 1;
q.push(s);
while(!q.empty())
{
int u = q.front(); q.pop();
if(vis[u]) continue;
vis[u] = 1;
int ch = 0;
for(int i = head[u]; i; i = e[i].next)
{
int v = e[i].v;
ch ++;
if(!vis[v]) q.push(v);
}
if(ch == 0)
{
// cout << u << endl;
for(int i = 0; i < m; i ++) V[i] = 0;
int yy = u;
while(yy)
{
if(V[b[yy]]) return 0;
V[b[yy]] = 1;
yy = fa[yy];
}
}
}
// for(int i = 1; i <= n; i ++)
// cout << b[i] << " ";
// cout << endl;
return 1;
}
inline void dfs(int x)
{
if(x == n + 1){if(pd()) ans ++;return ;}
for(int i = 1; i < m; i ++)
b[x] = i, dfs(x + 1);
return ;
}
signed main()
{
n = read(), m = read();
int fm = 1;
for(int i = 2; i < m; i ++) fm *= i;
for(int i = 2; i <= n; i ++)
{
fa[i] = read();
if(fa[i] != 1) ff = 1;
add(fa[i], i);
}
if(ff == 0) cout << stling(n - 1, m - 1) << endl;
else {dfs(2); cout << ans / fm << endl;}
return 0;
}
T3
二分答案 \(mid\),检查是否存在合法方案使得根节点答案大于等于 \(mid\)。
设 \(f[x]\) 表示最少在 \(x\) 的子树内分配多少个大于等于 \(mid\) 的叶子权值,使得 \(x\) 的权值大于等于 \(mid\)。
那么就是选择 \(\frac{k}{2}\) 个儿子节点,那么 \(f[x]=\sum f[y]\)。所以将儿子按照 \(f[x]\) 排序,从小到大选择,最终判断 \(f[x]\) 是否小于等于可分配的叶子权值个数。
#include<bits/stdc++.h>
#define int long long
#define Inf 1000010
#define N 200010
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;}
int n, k, fa[N], leafv[N], mid, res, sz[N], f[N];
vector<int> e[N];
void dfs(int u)
{
if(u > n - k)
{
if(leafv[u]) (f[u] = leafv[u] < mid? Inf : 0), sz[u] = 0;
else f[u] = sz[u] = 1;
return ;
}
sz[u] = 0;
vector<int> vec;
for(int v : e[u])
{
dfs(v); sz[u]+=sz[v];
vec.push_back(f[v]);
}
sort(vec.begin(), vec.end());
int s = 0;
for(int i = 0; i <= vec.size() / 2; i ++) s += vec[i];
f[u] = (s > sz[u] ? Inf : s);
}
inline int check()
{
int Res = k - mid + 1;
for(int i = n - k + 1; i <= n; i ++) Res -= (leafv[i] >= mid);
dfs(1);
return (f[1] <= Res);
}
signed main()
{
n = read(), k = read();
for(int i = 2; i <= n; i ++) fa[i] = read(), e[fa[i]].push_back(i);
for(int i = n - k + 1; i <= n; i ++) leafv[i] = read();
int L = 0, R = n + 1;
while(L + 1 < R)
{
mid = (L + R) / 2;
if(check()) L = mid;
else R = mid;
}
cout << L << endl;
return 0;
}
T4
看到有 25 的暴力分,直接暴力。
1 直接自己一个集合,也就是枚举 \(n-1\) 个点分到 \(m-1\) 个点里,这样复杂度降低不少。我们枚举的不能重复,所以枚举完最后要除以 \((m-1)!\)。
后面有 30 分特殊性质,直接第二类斯特林数 \(O(nm)\) 递推过去就行。
#include <bits/stdc++.h>
//#define umap unordered_map
#define P 1000000007
#define int long long
#define N 100010
using namespace std;
//umap<int, int>;
int n, m, fa[N], f[N][110], b[N], ans, ff, head[N], cnt, vis[N], V[N];
struct sb{int u, v, next;}e[N];
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;}
inline int stling(int p, int k)
{
f[0][1] = 1;
for(int i = 1; i <= p; i ++)
for(int j = 1; j <= min(i, k); j ++)
f[i][j] = (f[i - 1][j] * j + f[i - 1][j - 1]) % P;
return f[p][k];
}
inline void add(int u, int v)
{
e[++ cnt] = (sb){u, v, head[u]};
head[u] = cnt;
return ;
}
inline int pd()
{
for(int i = 0; i < m; i ++) V[i] = 0;
for(int i = 1; i <= n; i ++) V[b[i]] = 1;
for(int i = 0; i < m; i ++) if(V[i] == 0) return 0;
queue<int> q;
for(int i = 1; i <= n; i ++) vis[i] = 0;
int s = 1;
q.push(s);
while(!q.empty())
{
int u = q.front(); q.pop();
if(vis[u]) continue;
vis[u] = 1;
int ch = 0;
for(int i = head[u]; i; i = e[i].next)
{
int v = e[i].v;
ch ++;
if(!vis[v]) q.push(v);
}
if(ch == 0)
{
// cout << u << endl;
for(int i = 0; i < m; i ++) V[i] = 0;
int yy = u;
while(yy)
{
if(V[b[yy]]) return 0;
V[b[yy]] = 1;
yy = fa[yy];
}
}
}
// for(int i = 1; i <= n; i ++)
// cout << b[i] << " ";
// cout << endl;
return 1;
}
inline void dfs(int x)
{
if(x == n + 1){if(pd()) ans ++;return ;}
for(int i = 1; i < m; i ++)
b[x] = i, dfs(x + 1);
return ;
}
signed main()
{
n = read(), m = read();
int fm = 1;
for(int i = 2; i < m; i ++) fm *= i;
for(int i = 2; i <= n; i ++)
{
fa[i] = read();
if(fa[i] != 1) ff = 1;
add(fa[i], i);
}
if(ff == 0) cout << stling(n - 1, m - 1) << endl;
else {dfs(2); cout << ans / fm << endl;}
return 0;
}
考虑从上到下分配集合,或者按 DFS 序分配,保证祖先在自己之前分配集合即可。那么有 \(f[x][y] = f[x - 1][y] \times (y - dep) +f[x - 1][y - 1]\),当 \(y \ge dep\) 时。
这代表放到之前的集合内(不能和祖先放在一起,其他的集合任意放),或者自己新建
一个集合。
#include <iostream>
#include <cstdio>
#include <cstring>
#define ll long long
#define int long long
using namespace std;
const int P = 1e9 + 7;
const int N = 100500, M = 105;
int h[N], ne[N], to[N], tot, cnt, m, n;
inline void add(int x, int y) {
ne[++tot] = h[x], to[h[x] = tot] = y;
}
int dp[N][M];
void dfs(int x, int dep) {
++cnt;
for (int i = dep; i <= m; ++i)
dp[cnt][i] = (dp[cnt - 1][i] * (i - dep + 1) + dp[cnt - 1][i - 1]) % P;
for (int i = h[x]; i; i = ne[i]) dfs(to[i], dep + 1);
}
signed main() {
cin>>n>>m;
dp[0][0] = 1;
for (int i = 2, f; i <= n; ++i) cin>>f,add(f, i);
dfs(1, 1);
cout<<dp[n][m]<<endl;
return 0;
}
.
本文来自博客园,作者:北烛青澜,转载请注明原文链接:https://www.cnblogs.com/Multitree/articles/17595068.html
The heart is higher than the sky, and life is thinner than paper.
浙公网安备 33010602011771号