NOIP模拟(11.07)T2 路径统计

路径统计

题目背景:

11.07 NOIP模拟T2

分析:dfs + tarjan

 

这种没有思维难度专注于码量和细心程度的题我也是服气······

讲题,我们可以比较轻松的发现,因为每个点一定有出边,并且一共有n条边,那么显然就会形成很多的基环内向树,那么我们可以先tarjan一发统计环上点走到每一个环上其他点的距离之和,将每条边反向,变成基环外向树,然后对于每一个点cur我们dfs它下面挂的那棵树,然后显然它子树中的任意点x到环上其他点的距离为xcur的距离加上cur到环上那个点的距离,那么我们只需要将xcur的距离乘上环上的点数再加上cur到环上所有点的距离就可以了。所以到现在思路就比较清晰了,我们先tarjan找到每一个环,然后统计每一个点到其所在环上的其他点的距离和,然后对于每一个点dfs下面的那棵外向树统计贡献就可以了。

(是不是感觉说的很简单,代码不长也就不到4k,呵呵哒)

Source:

/*
	created by scarlyw
*/
#include <cstdio>
#include <iostream>
#include <cmath>
#include <algorithm>
#include <string>
#include <cstring>
#include <cctype>
#include <vector>
#include <queue>
#include <set>

const int MAXN = 500000 + 10;
const int mod = 1000000000 + 7;

struct node {
	int to, w;
	node(int to = 0, int w = 0) : to(to), w(w) {}
};

std::vector<node> edge[MAXN];
std::vector<int> s;
std::vector<int> sc[MAXN];
int o, n, ind, top;
int t[MAXN], l[MAXN], low[MAXN], num[MAXN], scc[MAXN];
int size[MAXN], d[MAXN], dis[MAXN];
int ans = 0;
long long sum;
bool vis[MAXN];

inline void read_in() {
	scanf("%d", &n);
	for (int i = 1; i <= n; ++i) 
		scanf("%d%d", &t[i], &l[i]), edge[t[i]].push_back(node(i, l[i]));
}

inline void tarjan(int cur) {
	low[cur] = num[cur] = ++ind, s.push_back(cur), vis[cur] = true;
	for (int p = 0; p < edge[cur].size(); ++p) {
		node *e = &edge[cur][p];
		if (num[e->to] == 0) 
			tarjan(e->to), low[cur] = std::min(low[cur], low[e->to]);
		else if (vis[e->to]) low[cur] = std::min(low[cur], num[e->to]);
	}
	if (low[cur] == num[cur]) {
		++top;
		do {
			o = s.back(), vis[o] = false, s.pop_back(), scc[o] = top;
			sc[top].push_back(o);
		} while (o != cur);
	}
}

inline void add(int &x, int t) {
	x += t, (x >= mod) ? (x -= mod) : 0;
}
inline void dfs(int cur) {
	if (vis[cur]) return ;
	vis[cur] = true, size[cur] = 1, d[cur] = 0;
	for (int p = 0; p < edge[cur].size(); ++p) {
		node *e = &edge[cur][p];
		if (scc[cur] != scc[e->to]) {
			dfs(e->to), size[cur] += size[e->to];
			add(d[cur], d[e->to]);
			add(d[cur], (long long)e->w * size[e->to] % mod);
		}
	}
	long long sz = sc[scc[cur]].size();
	add(ans, sz * d[cur] % mod);
	add(ans, (long long)(size[cur] - 1) * dis[cur] % mod);
	sum += sz * (size[cur] - 1);
}

inline void solve() {
	static int temp[MAXN];
	for (int i = 1; i <= n; ++i) if (num[i] == 0) tarjan(i);
	for (int i = 1; i <= top; ++i) {
		if (sc[i].size() == 1) dis[sc[i][0]] = 0;
		else {
			long long sz = sc[i].size();
			int cur = sc[i][0], x = cur;
			int sum = 0;
			temp[cur] = 0;
			do {
				temp[t[x]] = (temp[x] + l[x]) % mod, x = t[x];
				add(sum, temp[x]);
			} while (x != cur);
			cur = sc[i][0], x = cur;
			dis[cur] = (sum - temp[cur] + mod) % mod;
			add(ans, dis[cur]);
			do {
				dis[t[x]] = (dis[x] + temp[cur]) % mod;
				add(dis[t[x]], mod - sz * l[x] % mod);
				x = t[x], add(ans, dis[x]);
			} while (t[x] != cur);
			::sum += sz * (sz - 1LL);
		}
	}
	for (int i = 1; i <= n; ++i) if (!vis[i]) dfs(i);
	std::cout << (ans - (1LL * n * (n - 1) - sum) % mod + mod) % mod;
}

int main() {
//	freopen("road.in", "r", stdin);
//	freopen("road.out", "w", stdout);
	read_in();
	solve();
	return 0;
}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
【1】项目代码完整且功能都验证ok,确保稳定可靠运行后才上传。欢迎下载使用!在使用过程中,如有问题或建议,请及时私信沟通,帮助解答。 【2】项目主要针对各个计算机相关专业,包括计科、信息安全、数据科学与大数据技术、人工智能、通信、物联网等领域的在校学生、专业教师或企业员工使用。 【3】项目具有较高的学习借鉴价值,不仅适用于小白学习入门进阶。也可作为毕设项目、课程设计、大作业、初期项目立项演示等。 【4】如果基础还行,或热爱钻研,可基于此项目进行二次开发,DIY其他不同功能,欢迎交流学习。 【注意】 项目下载解压后,项目名字和项目路径不要用中文,否则可能会出现解析不了的错误,建议解压重命名为英文名字后再运行!有问题私信沟通,祝顺利! 基于C语言实现智能决策的人机跳棋对战系统源码+报告+详细说明.zip基于C语言实现智能决策的人机跳棋对战系统源码+报告+详细说明.zip基于C语言实现智能决策的人机跳棋对战系统源码+报告+详细说明.zip基于C语言实现智能决策的人机跳棋对战系统源码+报告+详细说明.zip基于C语言实现智能决策的人机跳棋对战系统源码+报告+详细说明.zip基于C语言实现智能决策的人机跳棋对战系统源码+报告+详细说明.zip基于C语言实现智能决策的人机跳棋对战系统源码+报告+详细说明.zip基于C语言实现智能决策的人机跳棋对战系统源码+报告+详细说明.zip基于C语言实现智能决策的人机跳棋对战系统源码+报告+详细说明.zip基于C语言实现智能决策的人机跳棋对战系统源码+报告+详细说明.zip基于C语言实现智能决策的人机跳棋对战系统源码+报告+详细说明.zip基于C语言实现智能决策的人机跳棋对战系统源码+报告+详细说明.zip基于C语言实现智能决策的人机跳棋对战系统源码+报告+详细说明.zip
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值