// // // // // // // // // // // // // //

9.15 校内模拟赛 题解报告

9.15 校内模拟赛 题解报告

\[\huge \%\%\%\%\%\ stO\ \ Zxsoul\ \ Orz\ \ \%\%\%\%\% \]

把对 zxsoul 神仙的膜拜打在最前面


福利场 + 图论专场

T1 并查集维护子树大小

T2 不知道考的啥的水题

T3 分层图

大概这场考试就是这样...


关于考试过程以及一些题外话

大概是遇到一场能做的 在 zxsoul 的无意提示下喜提 AK


这次的题确实是水...

开考从 T1 开始看... 读完题发现好像可以直接写...

想了一下 可以倒序建边 然后好像就做完了...

以为自己想得太简单了... 就先扔掉了 跑去看 T2

T2 好像可以直接倒序建图然后遍历一遍就行了...

是不是想的太简单了... 不管了 看 T3

T3 送了五十的最短路... 后面的分没什么思路

然后回头把 T1 写了 顺便把 T2 也写了...

然后把 T3 的五十写了...

害怕 T1 不对 然后开始想 T1 的暴力怎么写...

发现好像要写树剖 思索了一下还是写了(因为太闲了)

然后开始拍...

T2 实在想不出怎么写暴力... 毕竟感觉自己写的就是暴力 然后就没拍...

继续开 T3

一直没什么进展... 直到十一点的时候(还剩半个小时...)

(BS 旁边)zxsoul: 这 T3 必是个分层图...

嗯?

分层图... 好像可以写...

不到二十分钟码完造数据卡了一下好像没什么问题 就交了

然后? 然后就过了


这就 AK 了???


得分情况

100 + 100 + 100 = 300

传说中的福利场


题解

T1 大魔法师

暴力比正解都难写...


考虑每条边在那些组合里面会成为最大值 统计这种组合的个数即可

首先考虑最大的一条边 不难发现所有经过这条边的路径取值均为这条边 而经过这条边的路径数量则为这条边两边的点数的乘积 统计过这条边之后 所有经过这条边的路径都不需要再统计了 所以可以直接把这条边删掉 然后继续统计删除后两棵树的边

具体实现的话将边从大到小排序 依次删除 统计这条边连接的两点所在子树的大小 计入答案即可

但是这样子树的大小不太好维护 所以考虑从小到大排序 倒序加边 显然这与删边是等价的 并查集维护即可


代码

/*
  Source: 大魔法师
  首先有个五十的暴力 n 方枚举点对 
  
  考虑正解 
  从大到小枚举删边 维护子树节点数量即可 
  
  节点数量比较难搞 考虑维护倒序加边的过程 
*/
#include<cstdio>
#include<cstring>
#include<algorithm>
#define int long long
#define pt putchar(' ')
#define pn putchar('\n')
#define Abs(x) ((x) < 0 ? -(x) : (x))
#define Max(x, y) ((x) > (y) ? (x) : (y))
#define Min(x, y) ((x) < (y) ? (x) : (y))
#define Swap(x, y) ((x) ^= (y) ^= (x) ^= (y))
/*----------------------------------------------------------*/
const int A = 1e4 + 7;
const int B = 2e5 + 7;
const int C = 1e6 + 7;
const int D = 1e7 + 7;
const int mod = 999999937;
const int INF = 0x3f3f3f3f;
/*----------------------------------------------------------*/
inline void File() {
	freopen("magic.in", "r", stdin);
	freopen("magic.out", "w", stdout);
}
/*----------------------------------------------------------*/
int n, siz[B], fa[B], ans;
struct node {int u, v, w;} a[B];
struct edge {int v, w, nxt;} e[B << 1];
int head[B], ecnt;
/*----------------------------------------------------------*/
inline int read() {
	int x = 0, f = 1; char ch = getchar();
	while(ch < '0' || ch > '9') {if(ch == '-') f = -1; ch = getchar();}
	while(ch >= '0' && ch <= '9') {x = (x << 3) + (x << 1) + (ch ^ 48); ch = getchar();}
	return x * f;
}
void Print(int x) {if(x < 0) putchar('-'), x = -x; if(x > 9) Print(x / 10); putchar(x % 10 ^ 48);}
/*----------------------------------------------------------*/
void add_edge(int u, int v, int w) {e[++ecnt] = (edge){v, w, head[u]}; head[u] = ecnt;}
int find(int x) {return fa[x] == x ? x : fa[x] = find(fa[x]);}
bool cmp(node x, node y) {return x.w < y.w;}
void Main() {
	File();
	n = read();
	for(int i = 1; i ^ n + 1; ++i) fa[i] = i, siz[i] = 1;
	for(int i = 1, x, y, z; i ^ n; ++i)
		a[i] = (node){x = read(), y = read(), z = read()}, add_edge(x, y, z), add_edge(y, x, z);
	std::sort(a + 1, a + n, cmp);
	for(int i = 1; i ^ n; ++i)
	{
		int x = find(a[i].u), y = find(a[i].v), w = a[i].w;
		fa[x] = y; ans = (ans + siz[x] * siz[y] % mod * w % mod) % mod; siz[y] += siz[x];
	}
	Print(ans);
}
/*----------------------------------------------------------*/
signed main() {Main(); return 0;}


T2 捷径

这场考试里最水的一道题


建反图 倒着贪即可

容易发现每个点第一次被搜到的时候是最优的 打标记 复杂度为 \(O(n)\)


/*
  Source: 捷径
  直接建图 然后倒着扫一遍即可 
*/
#include<cstdio>
#include<cstring>
#define pt putchar(' ')
#define pn putchar('\n')
#define Abs(x) ((x) < 0 ? -(x) : (x))
#define Max(x, y) ((x) > (y) ? (x) : (y))
#define Min(x, y) ((x) < (y) ? (x) : (y))
#define Swap(x, y) ((x) ^= (y) ^= (x) ^= (y))
/*----------------------------------------------------------*/
const int A = 1e4 + 7;
const int B = 1e5 + 7;
const int C = 1e6 + 7;
const int D = 1e7 + 7;
const int mod = 1e9 + 7;
const int INF = 0x3f3f3f3f;
/*----------------------------------------------------------*/
inline void File() {
	freopen("contra.in", "r", stdin);
	freopen("contra.out", "w", stdout);
}
/*----------------------------------------------------------*/
int n, m, ans[B];
struct edge {int v, nxt;} e[B];
int head[B], ecnt;
bool vis[B];
/*----------------------------------------------------------*/
inline int read() {
	int x = 0, f = 1; char ch = getchar();
	while(ch < '0' || ch > '9') {if(ch == '-') f = -1; ch = getchar();}
	while(ch >= '0' && ch <= '9') {x = (x << 3) + (x << 1) + (ch ^ 48); ch = getchar();}
	return x * f;
}
void Print(int x) {if(x < 0) putchar('-'), x = -x; if(x > 9) Print(x / 10); putchar(x % 10 ^ 48);}
/*----------------------------------------------------------*/
void add_edge(int u, int v) {e[++ecnt] = (edge){v, head[u]}; head[u] = ecnt;}
void dfs(int u, int tp) {
	ans[u] = tp; vis[u] = 1;
	for(int i = head[u], v; i; i = e[i].nxt) if(!vis[v = e[i].v]) dfs(v, tp);
}
void Main() {
	File();
	n = read(); m = read();
	for(int i = 1, x, y; i ^ m + 1; ++i) x = read(), y = read(), add_edge(y, x);
	for(int i = n; i; --i) if(!vis[i]) dfs(i, i);
	for(int i = 1; i ^ n + 1; ++i) Print(ans[i]), pt; 
}
/*----------------------------------------------------------*/
signed main() {Main(); return 0;}

T3 过路费

建分层图跑最短路即可


代码

/*
  Source: 过路费
*/
#include<queue>
#include<cstdio>
#include<cstring>
#include<algorithm>
#define int long long
#define pt putchar(' ')
#define pn putchar('\n')
#define pr std::pair <int, int> 
#define mk std::make_pair 
#define Abs(x) ((x) < 0 ? -(x) : (x))
#define Max(x, y) ((x) > (y) ? (x) : (y))
#define Min(x, y) ((x) < (y) ? (x) : (y))
#define Swap(x, y) ((x) ^= (y) ^= (x) ^= (y))
/*----------------------------------------------------------*/
const int A = 1e4 + 7;
const int B = 5e4 + 7;
const int C = 1e6 + 7;
const int D = 1e7 + 7;
const int mod = 1e9 + 7;
const int INF = 0x3f3f3f3f;
/*----------------------------------------------------------*/
inline void File() {
	freopen("pass.in", "r", stdin);
	freopen("pass.out", "w", stdout);
}
/*----------------------------------------------------------*/
int n, m, K, S, T, dis[2][A * 13], ans = INF;
struct edge {int v, w, nxt;} e[C << 2];
int head[A * 13], ecnt;
bool vis[A * 13];
std::priority_queue <pr> q;
/*----------------------------------------------------------*/
inline int read() {
	int x = 0, f = 1; char ch = getchar();
	while(ch < '0' || ch > '9') {if(ch == '-') f = -1; ch = getchar();}
	while(ch >= '0' && ch <= '9') {x = (x << 3) + (x << 1) + (ch ^ 48); ch = getchar();}
	return x * f;
}
void Print(int x) {if(x < 0) putchar('-'), x = -x; if(x > 9) Print(x / 10); putchar(x % 10 ^ 48);}
/*----------------------------------------------------------*/
void add_edge(int u, int v, int w) {e[++ecnt] = (edge){v, w, head[u]}; head[u] = ecnt;}
void dijk(int s, bool op) {
	for(int i = 0; i ^ n + 1; ++i) dis[op][i] = INF, vis[i] = 0;
	dis[op][s] = 0; q.push(mk(0, s));
	while(!q.empty())
	{
		int u = q.top().second; q.pop(); if(vis[u]) continue; vis[u] = 1;
		for(int i = head[u], v; i; i = e[i].nxt) if(dis[op][v = e[i].v] > dis[op][u] + e[i].w)
			dis[op][v] = dis[op][u] + e[i].w, q.push(mk(-dis[op][v], v));
	}
}
void dijk2(int s, bool op) {
	dis[op][s] = 0; q.push(mk(0, s));
	while(!q.empty())
	{
		int u = q.top().second; q.pop(); if(vis[u]) continue; vis[u] = 1;
		for(int i = head[u], v; i; i = e[i].nxt) if(dis[op][v = e[i].v] > dis[op][u] + e[i].w)
			dis[op][v] = dis[op][u] + e[i].w, q.push(mk(-dis[op][v], v));
	}
}
void work1() {dijk(S, 0); Print(dis[0][T]);}
void work2() {
	dijk(S, 0); dijk(T, 1);
	for(int i = 1; i <= 2 * m; i += 2)
	{
		int u = e[i + 1].v, v = e[i].v, tmp = Min(dis[0][u] + dis[1][v], dis[0][v] + dis[1][u]);
		ans = Min(ans, tmp);
	}
	Print(ans);
}
void work3() {
	for(int i = 1; i ^ m + 1; ++i)
	{
		int x = read(), y = read(), z = read();
		add_edge(x, y, z); add_edge(y, x, z);
		for(int j = 1; j ^ K + 1; ++j)
		{
			add_edge(x + j * n, y + j * n, z);
			add_edge(y + j * n, x + j * n, z);
			
			add_edge(x + (j - 1) * n, y + j * n, 0);
			add_edge(y + (j - 1) * n, x + j * n, 0);
		}
	}
	for(int i = 0; i ^ K + 1; ++i) add_edge(T + i * n, (K + 1) * n + 1, 0);
	memset(dis, 63, sizeof dis); memset(vis, 0, sizeof vis); dijk2(S, 0);
	Print(dis[0][(K + 1) * n + 1]);
}
void Main() {
	File();
	n = read(); m = read(); K = read(); S = read(); T = read();
	work3(); return ;
	if(K != 0 && K != 1) {work3(); return ;}
	for(int i = 1, x, y, z; i ^ m + 1; ++i)
		x = read(), y = read(), z = read(), add_edge(x, y, z), add_edge(y, x, z);
	if(!K) work1();
	else if(K == 1) work2();
//	else work3();
//	work3();
}
/*----------------------------------------------------------*/
signed main() {Main(); return 0;}


后记

梦想太抽象, 现实太真实, 十七岁了, 祝自己生日快乐. —— 2021.9.15

posted @ 2021-09-15 14:41  Blank_space  阅读(63)  评论(10编辑  收藏  举报
// // // // // // //