NOIP模拟赛R8

A

绷,看错题导致自己被硬控 1 个小时。

其实也还好,题目问你最多可以被分成多少段,按照贪心不难想到要尽可能让每一段的和变小。

这个时候考虑前缀和 \(sum_i\),不难发现,如果要一段的和 \(\geq 0\),只需要让 $sum_i \geq sum_j $ 并且 \(i > j\)

于是考虑邪修方法从后往前看,发现只需要记录当前看到的所有数字的最小值的位置,然后统计个数即可。

#include <bits/stdc++.h>
using namespace std;
#define ll long long
#define ull unsigned long long
const int INF = 1e9+100;void solve () {int n; cin >> n;vector <int> a(n+1), sum(n+1);for (int i = 1;i <= n;i++) {cin >> a[i];sum[i] = sum[i-1] + a[i];}vector <int> ans;int minn = INF;for (int i = n;i >= 1;i--) if (sum[i] <= minn) ans.push_back(sum[i]), minn = sum[i];cout << ans.size() << "\n";
}int main () {ios::sync_with_stdio(false);cin.tie(nullptr), cout.tie(nullptr);int _ = 1; while (_--) solve();return 0;
}

B

又是被图论杀死的一天。

手推样例,不难发现最优解就是从一个点出发到所有点然后再从所有点返回,而且不是 bfs 那种一层层往下递进的,而是一个一个点地走,类似拓扑排序,事实上还真是拓扑。

假设一个子树的根节点为 \(u\),不难发现,如果从 \(u\) 开始拓扑和从叶子节点开始拓扑,都会有一样数量的答案,而这两种情况的答案可以随机组合,所以不难发现对于一个子树而言,他的答案就是拓扑序的数量的平方。

拓扑序的公式我们先记住吧,\(\frac{size_u!}{\prod\text{u的所有子树的大小}}\)

所以我们可以用 \(dp_i\) 表示以 \(i\) 为根的子树的 \(\prod\text{u的所有子树大小}\) 的逆元,然后对于每个节点去做一次换根 dp,转移方程挺好想,因为当转移到根节点的一个儿子时,以那个儿子为根的子树已经变为了整棵树,而这个逆元我们可以不动,先把再转移前搞得儿子的子树节点数量的逆元给他抵消,然后加上新的以原来根节点为根的子树的逆元即可(这里 \(T_u\) 表示 \(u\) 的子树集合):

\[dp_u = \frac{size_u!}{\prod_{i \in T_u}size_i} \]

\[dp_v = \frac{size_u!}{\prod_{i \in T_u}size_i} \times size_v \times \frac{1}{n-size_v} \]

#include <bits/stdc++.h>
using namespace std;
#define ll long long
#define ull unsigned long long
const ll MOD = 1e9+7;
const ll N = 2e5+1000;ll fac[N], inc[N];void solve () {ll n; cin >> n;vector <vector <ll>> E(n+1);vector <ll> dp(n+1), si(n+1);for (ll i = 1;i < n;i++) {ll a, b; cin >> a >> b;E[a].push_back(b), E[b].push_back(a);} ll root = (n+1)/2;ll ans = 0;auto dfs = [&](auto self, ll fa, ll now) -> void {dp[now] = si[now] = 1;for (auto to : E[now]) {if (to == fa) continue;self(self, now, to);dp[now] = (dp[now]*dp[to])%MOD;si[now] += si[to];}dp[now] = (dp[now]*inc[si[now]]%MOD)%MOD;};auto count = [&](auto self, ll fa, ll now, ll tmp) -> void {ans = (ans + tmp*tmp%MOD) % MOD;ll _tmp = 0;for (auto to : E[now]) {if (to == fa) continue;_tmp = (tmp*inc[n-si[to]]%MOD*si[to])%MOD;self(self, now, to, _tmp);}};dfs(dfs, -1, root);count(count, -1, root, dp[root]*fac[n]%MOD);cout << ans%MOD << "\n";
}int main () {ios::sync_with_stdio(false);cin.tie(nullptr), cout.tie(nullptr);fac[0] = fac[1] = inc[1] = 1;for (ll i = 2;i < N;i++) fac[i] = (i*fac[i-1])%MOD,inc[i] = (MOD-MOD/i)*inc[MOD%i]%MOD;ll _ = 1; while (_--) solve();return 0;
}

C

逆天东西。

类似扫描线的思路?反正得先离散坐标,然后从左到右逐个删掉左侧的点,统计出包含任意点的矩形数量,需要用乘法原理。这里插播一条,假设有能分别包含两个点的 \(y\) 范围为 \([top_1, bottom_1], [top_2, bottom_2]\),不难发现如果假设底下的方案数为 \(cnt_i\),有结果:

\[cnt_i \times (top_1-bottom_1) + cnt_i \times (top2-bottom_2) \]

所以可以用乘法分配律,直接全部加上即可。

#include <bits/stdc++.h>
using namespace std;
#define ll long long
#define ull unsigned long long
#define pii pair <int, int> 
const int MOD = 1e9+7;void solve () {int n, m, k; cin >> n >> m >> k;vector <pii> pos(k+10);vector <int> val(k+10);for (int i = 1;i <= k;i++) {cin >> pos[i].first >> pos[i].second;val[i] = pos[i].first;}sort(pos.begin()+1, pos.begin()+k+1,[&](pii x, pii y) { return (x.first != y.first ? x.first < y.first : x.second < y.second); } );sort(val.begin()+1, val.begin()+k+1);int tot = unique(val.begin()+1, val.begin()+1+k)-val.begin(); // cout << tot << "\n";val[0] = 0, val[tot] = n+1;ll ans = 0;for (int i = 1;i < tot;i++) {ll tmp = 0;int pre = val[i] - val[i-1];int r = 1;while (r <= k && pos[r].first < val[i]) r++;set <int> s;s.insert(0), s.insert(m+1);for (int j = i;j < tot;j++) {int top, bot;for (;r <= k && pos[r].first == val[j];++r) {if (s.count(pos[r].second)) continue;set <int>::iterator now = s.lower_bound(pos[r].second);top = *now, bot = *(--now);tmp = (tmp + (ll)(top-pos[r].second)*(pos[r].second-bot)%MOD)%MOD;s.insert(pos[r].second);}int _tmp = val[j+1]-val[j];ans = (ans + tmp*_tmp%MOD*pre%MOD)%MOD;}}cout << ans << "\n";
}int main () {ios::sync_with_stdio(false);cin.tie(nullptr), cout.tie(nullptr);int _ = 1; while (_--) solve();return 0;
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/944167.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!