【luogu P7293】Sum of Distances P(线段树)(图论)

Sum of Distances P

题目链接:luogu P7293

题目大意

给你 k 个图,然后构建一个新图,新图点数是前面几个图点数的乘积。
每个点用 k 元组表示,然后如果有两个 k 元组,它们每一元在对于的图上都有边,那这两个点之间就连边。
然后问你跟 (1,1,...,1) 在同一个连通块的点每个点到它的最短路径之和。

思路

首先不难想到是可以在一个边反复横跳以等另一个图的边走到某个点的。
所以我们可以求出每个图每个点的奇偶最短路。

然后你考虑要怎么求答案,对于一个点 (a1,a2,...,ak),就是 min{max{disai,0},max{disai,1}}

然后你发现两个 max 可以单独搞,但是这个 min 就不太友好,考虑搞一搞,变成:
max{disai,0}+max{disai,1}max{max{disai,0,disai,1}}

那三个可以分别搞,那要怎么搞呢?
你可以枚举 k,使得第 k 元贡献的最终的答案。
那你就是枚举这一元选的,然后在其他里面看有多少个小于它的,然后个数乘积起来就是贡献。
考虑搞一个线段树维护每个图里面现在有多少个点小于,然后区间统计的就是区间值的乘积。
然后一开始全部插入,然后从大到小枚举点,统计之后就把贡献删掉。

然后就可以了。
(记得求第三个的时候如果取 maxINF 的话这个点不要算进去)

代码

#include<queue> #include<cstdio> #include<vector> #include<cstring> #include<iostream> #define mo 1000000007 #define ll long long #define INF 0x3f3f3f3f3f3f3f3f using namespace std; struct node { int to, nxt; }e[800001]; int k, n[50001], m[50001], count[500001], mxn[50001]; int le[200001], KK, x, y, dis[200001], cnt[500001]; int nmb[200001]; vector <ll> minn[50001][2]; vector <pair<int, int> > tong[400001]; bool in[200001]; ll ans; struct abab { int x; }; bool operator <(abab x, abab y) { return mxn[x.x] > mxn[y.x]; } priority_queue <abab> mg; void add(int x, int y) { e[++KK] = (node){y, le[x]}; le[x] = KK; e[++KK] = (node){x, le[y]}; le[y] = KK; } struct ztzt { int dis, now; }; bool operator <(ztzt x, ztzt y) { return x.dis > y.dis; } priority_queue <ztzt> q; void dij(int x) {//求奇偶最短路 while (!q.empty()) q.pop(); dis[0] = INF; for (int i = 1; i <= n[x]; i++) { dis[i] = dis[i + n[x]] = INF; in[i] = in[i + n[x]] = 0; } dis[1] = 0; q.push((ztzt){0, 1}); while (!q.empty()) { int now = q.top().now; q.pop(); if (in[now]) continue; in[now] = 1; for (int i = le[now]; i; i = e[i].nxt) if (!in[e[i].to] && dis[e[i].to] > dis[now] + 1) { dis[e[i].to] = dis[now] + 1; q.push((ztzt){dis[e[i].to], e[i].to}); } } } struct XDtree {//线段树维护 ll val[500001 << 2]; void clean() { memset(val, 0, sizeof(val)); } void up(int now) { val[now] = val[now << 1] * val[now << 1 | 1] % mo; } void insert(int now, int l, int r, int pl, int va) { if (l == r) { val[now] = (val[now] + va + mo) % mo; return ; } int mid = (l + r) >> 1; if (pl <= mid) insert(now << 1, l, mid, pl, va); else insert(now << 1 | 1, mid + 1, r, pl, va); up(now); } ll query(int now, int l, int r, int L, int R) { if (L > R) return 1; if (L <= l && r <= R) { return val[now]; } int mid = (l + r) >> 1; ll re = 1; if (L <= mid) re = (re * query(now << 1, l, mid, L, R)) % mo; if (mid < R) re = (re * query(now << 1 | 1, mid + 1, r, L, R)) % mo; return re; } }T; ll get_max(int op) { ans = 0; int maxn = -1; for (int i = 1; i <= k; i++) for (int j = 0; j < n[i]; j++) { int vl = -1; if (op == 1) vl = minn[i][0][j]; else if (op == 2) vl = minn[i][1][j]; else { vl = max(minn[i][0][j], minn[i][1][j]); // if (vl == dis[0]) vl = min(minn[i][0][j], minn[i][1][j]); } if (vl == dis[0]) continue;//记得不要把这种给统计进去 nmb 里面 nmb[i]++; tong[vl].push_back(make_pair(i, j)); maxn = max(maxn, vl); } for (int i = 1; i <= k; i++) { T.insert(1, 1, k, i, nmb[i]); } for (int i = maxn; i >= 1; i--) { for (int j = 0; j < tong[i].size(); j++) { ans = (ans + i * (T.query(1, 1, k, 1, tong[i][j].first - 1) * T.query(1, 1, k, tong[i][j].first + 1, k) % mo) % mo) % mo;//剩余部分的乘积 T.insert(1, 1, k, tong[i][j].first, -1);//减少 } } for (int i = 1; i <= maxn; i++) tong[i].clear(); T.clean(); memset(nmb, 0, sizeof(nmb)); return ans; } int main() { scanf("%d", &k); for (int i = 1; i <= k; i++) { scanf("%d %d", &n[i], &m[i]); KK = 0; for (int j = 1; j <= 2 * n[i]; j++) le[j] = 0; for (int j = 1; j <= m[i]; j++) { scanf("%d %d", &x, &y); add(x, y + n[i]); add(x + n[i], y); } dij(i); for (int j = 1; j <= n[i]; j++) minn[i][0].push_back(dis[j]), minn[i][1].push_back(dis[j + n[i]]); } printf("%lld", (get_max(1) + get_max(2) - get_max(3) + mo) % mo); return 0; }

__EOF__

本文作者あおいSakura
本文链接https://www.cnblogs.com/Sakura-TJH/p/luogu_P7293.html
关于博主:评论和私信会在第一时间回复。或者直接私信我。
版权声明:本博客所有文章除特别声明外,均采用 BY-NC-SA 许可协议。转载请注明出处!
声援博主:如果您觉得文章对您有帮助,可以点击文章右下角推荐一下。您的鼓励是博主的最大动力!
posted @   あおいSakura  阅读(32)  评论(0编辑  收藏  举报
编辑推荐:
· go语言实现终端里的倒计时
· 如何编写易于单元测试的代码
· 10年+ .NET Coder 心语,封装的思维:从隐藏、稳定开始理解其本质意义
· .NET Core 中如何实现缓存的预热?
· 从 HTTP 原因短语缺失研究 HTTP/2 和 HTTP/3 的设计差异
阅读排行:
· 分享一个免费、快速、无限量使用的满血 DeepSeek R1 模型,支持深度思考和联网搜索!
· 基于 Docker 搭建 FRP 内网穿透开源项目(很简单哒)
· ollama系列01:轻松3步本地部署deepseek,普通电脑可用
· 25岁的心里话
· 按钮权限的设计及实现
点击右上角即可分享
微信分享提示