【tyvj】“扫地“杯 Day1 题解

校内模拟赛("扫地"杯 Day 1?)

道路修建

题面:

题目背景

liouzhou_101最悲痛的回忆就是NOI2011的道路修建,当时开了系统堆栈,结果无限RE…

题目描述

出于某种报复心理,就把那题神奇了一下:

在Z星球上有N个国家,这N个国家之间只能建造N-1条道路且全部建完这N-1条道路后这N个国家相互连通,修建每条道路都有相应的花费。但是他们都很吝啬,于是决定只随机选出两个不同的国家(为了国家的平等,当然这两个国家是无顺序可言的),建造该建造的道路,使得这两个国家相互连通,自然费用越少越好。然后问你,在所有情况中,修建道路花费的平均值。

假若您认为本题题目叙述太渣,那就形象地描述一遍:给出一棵边上带权的树,求任意两个不同的点的距离的期望值。

输入格式

第一行包括一个正整数N,N表示国家的数量。

接下来N-1行每行包括三个正整数x、y和w,表示国家x和国家y之间有一条花费为w的道路。

输出格式

仅一行,包含一个最简分数,格式为A/B,详见样例。

样例 #1
样例输入 #1
4
1 2 1
1 3 1
1 4 1
样例输出 #1
3/2
提示

【提示】

共存在6种情况:

①(1,2) 距离 1;

②(1,3) 距离 1;

③(1,4) 距离 1;

④(2,3) 距离 2;

⑤(2,4) 距离 2;

⑥(3,4) 距离 2;

所以平均值为(1+1+1+2+2+2)/6=3/2。

30%的数据,满足n<=1,000;

50%的数据,满足n<=10,000;

100%的数据,满足1<=n<=100,000,1<=w<=1,000。

【时限】

1s

计数题,我们求期望时考虑将贡献拆开:我们算每条边会经过多少次!

我们每条边对于一个父亲点指向儿子点

我们发现一条边被经过,只有选中上述的儿子点内的子树和子树外的一点形成的路径

所以可以树剖(只是小dfs)后对每个点子树内的大小 × \times × 子树外的大小 → ans ⁡ \rightarrow \operatorname{ans} ans,即:

ans ⁡ ← ∑ i : 1 ∼ n siz ⁡ i ( n − siz ⁡ i ) \operatorname{ans} \leftarrow \sum_{i:1\sim n} \operatorname{siz}_i (n - \operatorname{siz}_i) ansi:1nsizi(nsizi)

因为树上一共有 ( n 2 ) \dbinom{n}{2} (2n) 条路径,所以答案为 2 ans ⁡ n ( n − 1 ) \frac{2\operatorname{ans}}{n(n - 1)} n(n1)2ans

AC-code:

#include<bits/stdc++.h>
using namespace std;
#define int long long
int rd() {
	int x = 0, w = 1;
	char ch = 0;
	while (ch < '0' || ch > '9') {
		if (ch == '-') w = -1;
		ch = getchar();
	}
	while (ch >= '0' && ch <= '9') {
		x = x * 10 + (ch - '0');
		ch = getchar();
	}
	return x * w;
}

void wt(int x) {
	static int sta[35];
	int f = 1;
	if(x < 0) f = -1,x *= f;
	int top = 0;
	do {
		sta[top++] = x % 10, x /= 10;
	} while (x);
	if(f == -1) putchar('-');
	while (top) putchar(sta[--top] + 48);
}

const int N = 1e5+5;
int n;
int head[N],nxt[N<<1],to[N<<1],val[N<<1],cnt;
void init() {memset(head,-1,sizeof(head));cnt = 0;}
void add(int u,int v,int w) {
	nxt[cnt] = head[u];
	to[cnt] = v;
	val[cnt] = w;
	head[u] = cnt++;
}

int siz[N],son[N],dep[N],fa[N],ans;

void dfs1(int x,int f) {
	fa[x] = f;
	siz[x] = 1;
	dep[x] = dep[f] + 1;
	for(int i = head[x];~i;i = nxt[i]) {
		int y = to[i],w = val[i];
		if(y ^ f) {
			dfs1(y,x);
			siz[x] += siz[y];
			ans += siz[y] * (n - siz[y]) * w;
			if(siz[son[x]] < siz[y]) son[x] = y;
		}
	}
}

signed main() {
	init();	
	n = rd();
	for(int i = 1;i<n;i++) {
		int u = rd(),v = rd(),w = rd();
		add(u,v,w);add(v,u,w);
	}
	dfs1(1,0);
	int mo = n * (n - 1) / 2;
	int gcd = __gcd(mo,ans);
	ans /= gcd;
	mo /= gcd;
	wt(ans),putchar('/'),wt(mo);
	return 0;
}

破坏基地

题面:

题目背景

在Z国和W国之间一直战火不断。好不容易,W国的间谍把完整的Z国的军事基地的地图到手了。于是W国决定再次出击,一举击破Z国的防线。

题目描述

W国认真研究了Z国的地形,发现Z国有N个军事基地,我们不妨编号成1…N,而且经过深刻研究,发现1号军事基地是资源补给基地,而N号军事基地是前线。由于地形的缘故,只有M对军事基地两两可达,当然是有距离的。此时W国的弹头紧缺,当下的弹头只能去毁灭一个军事基地。当然了,最重要的就是毁灭一个军事基地,使得资源补给基地与前线的最短距离发生变化。但是Z国也不是白痴,他们的资源补给基地与前线有着极高的防御力,所以W国只能去炸掉其余的N-2个基地,当然炸掉某个基地后,这个基地就不可达了。于是问题就来了,炸掉哪些基地后会使得资源补给基地与前线的最短距离发生变化呢?

注:假若炸掉某个基地后,1号基地和N号基地不连通,那么我们也认为他们的最短距离发生了变化。

输入格式

输入数据第一行是两个正整数N,M,意义如题所述。

接下来M行,每行包括三个正整数x,y,d,表示有一条边连接x、y两个地点,其距离为d。

数据保证图是连通的。

输出格式

输出数据的第一行包含一个整数K,表示有K个基地可毁灭,且毁灭其中任意一个后,资源补给基地与前线的最短距离发生变化。

接下来K行,每行输出一个军事基地的编号,要求编号递增。

在wyl8899神犇的率领下,W国必胜!!!
因此一定不会存在K=0的情况。

样例 #1
样例输入 #1
6 7
1 2 3
1 5 2
2 3 5
2 4 3
2 5 4
2 6 5
3 4 2
样例输出 #1
1
2
提示

【提示】

对于30%的数据,N<=100,M<=1,000;

对于60%的数据,N<=2,000,M<=20,000;

对于100%的数据,N<=10,000,M<=100,000,1<=d<=10,000;

【时限】

1s

一眼,最短路计数

首先可以确定的一点是,更改点只能是最短路上的点,因为改其他的根本不会有影响!

然后,更改点还得经过所有最短路!不然还有漏网的最短路!

为确定哪些点经过所有最短路,我们从 s , t s,t s,t 两边跑最短路计数:

如图:

img

我们发现,当一个点两个最短路计数乘积等于 s ⇝ t s \leadsto t st 最短路个数时,这个点在所有最短路上

本题跑 dij,然后回溯最短路求解(Tip:注意输出格式

AC-code:

#include<bits/stdc++.h>
using namespace std;
#define int long long		
int rd() {
	int x = 0, w = 1;
	char ch = 0;
	while (ch < '0' || ch > '9') {
		if (ch == '-') w = -1;
		ch = getchar();
	}
	while (ch >= '0' && ch <= '9') {
		x = x * 10 + (ch - '0');
		ch = getchar();
	}
	return x * w;
}

void wt(int x) {
	static int sta[35];
	int f = 1;
	if(x < 0) f = -1,x *= f;
	int top = 0;
	do {
		sta[top++] = x % 10, x /= 10;
	} while (x);
	if(f == -1) putchar('-');
	while (top) putchar(sta[--top] + 48);
}
const int N = 1e4+5,M = 1e5+5;
int n,m;
int head[N],nxt[M<<1],to[M<<1],val[M<<1],cnt;
void init() {memset(head,-1,sizeof(head));cnt = 0;}
void add(int u,int v,int w) {
	nxt[cnt] = head[u];
	to[cnt] = v;
	val[cnt] = w;
	head[u] = cnt++;
}

vector<int> c[N];
int dis[N],v[N],p[N];
bool vis[N];
priority_queue<array<int,2>,vector<array<int,2>>,greater<array<int,2>>> q;

void dij(int s) {
	memset(dis,0x3f,sizeof(dis));
	memset(vis,0,sizeof(vis));
	memset(v,0,sizeof(v));
	dis[s] = 0;v[s] = 1;
	q.push(array<int,2>{0,s});
	while(q.size()) {
		array<int,2> t = q.top();
		q.pop();
		if(vis[t[1]]) continue;
		vis[t[1]] = true;
		int x = t[1];
		for(int i = head[x];~i;i = nxt[i]) {
			int y = to[i],w = val[i];
			if(dis[y] == dis[x] + w) {
				v[y] += v[x];
				if(s == 1)c[y].emplace_back(x);
			}
			if(dis[y] > dis[x] + w) {
				dis[y] = dis[x] + w;
				v[y] = v[x];
				if(s == 1)c[y].clear();
				if(s == 1)c[y].emplace_back(x);
				q.emplace(array<int,2>{dis[y],y});
			}
		}
	}
}


set<int> ans;
void dfs(int s) {
	for(int t : c[s]) {
		if(t == 1) continue;
		if(v[t] * p[t] == p[n])
			ans.emplace(t);
		dfs(t);
	}
}

signed main() {
	init();
	n = rd(),m = rd();
	for(int i = 1;i<=m;i++) {
		int u = rd(),v = rd(),w = rd();
		add(u,v,w);add(v,u,w);
	}
	dij(1);
	memcpy(p,v,sizeof(p));
	dij(n);
	dfs(n);	
	wt(ans.size());putchar('\n');
	for(int x : ans) wt(x),putchar('\n');
	return 0;
}
/*
12 16
1 9 1
9 8 1
8 5 1
8 6 1
6 2 2
6 3 2 
6 4 2
5 2 2
5 3 2
5 4 2
2 10 5
3 10 5
4 10 5
10 12 100
1 11 2
11 6 1
*/

诡异的数学题

题面:

题目背景

liouzhou_101 从 NOI 归来后文化课像坨屎,于是决定去补做一些作业,结果翻开作业的第一题就傻眼了

题目描述

设 a、b、c 为实数,且满足 a+b+c=15,a2+b2+c^2=100,则 a 的最大值和最小值的积为____。

话说这题他都没有想出来怎么做,就开始自己 YY,把这题牛逼成了:
设 a1、a2、…、an 为实数,且 a1+a2+…+an=x,a1^2 + a2^2 +…+an^2=y,则 a1 的最大值和最小
值的积为____。

liouzhou_101 这种傻×自然不会做了,于是来向你请教

输入格式

输入的第一行是一个正整数 T,表示测试组数。

接着对每组测试数据,输入只有一行,三个正整数 N、x 和 y,之间以一个空格隔开

输出格式

对于每组测试数据,输出只有一行,假若不存在满足题目要求的,就输出“WA RE CE TLE MLE OLE”(不含引号);否则输出一个精确到小数点后 6 位的浮点数,即 a1 的最大值和最小值的积。

样例 #1
样例输入 #1
2
3 15 100
1 4 15
样例输出 #1
8.333333
WA RE CE TLE MLE OLE
提示

【提示】

对于第一组,a1 最大是 9.082483,最小是 0.917517,乘起来就是 8.333333。

对于第二组,明显不可能存在 a1 满足题意。

对于 10%的数据,N=1;

对于 20%的数据,N<=2;

对于 40%的数据,N<=3;

对于 70%的数据,N<=100;

对于 100%的数据,1<=T<=10,

1<=N<=10,000,0<=x<=10,000,0<=y<=10,000。

【时限】

1s

你得会大名鼎鼎的均值不等式

我会,但是我没做出来!鉴定为思维不够

先看 n n n 元均值不等式:
n ∑ i = 1 n 1 a i ≤ ∏ i = 1 n a i n ≤ ∑ i = 1 n a i n ≤ ∑ i = 1 n a i 2 n \frac{n}{\sum_{i = 1}^n\frac{1}{a_i}} \leq \sqrt[n]{\prod_{i = 1}^na_i} \leq \frac{\sum_{i = 1}^na_i}{n} \leq \sqrt{\frac{\sum_{i = 1}^na_i^2}{n}} i=1nai1nni=1nai ni=1naini=1nai2

因为 x = ∑ i = 1 n a i , y = ∑ i = 1 n a i 2 x = \sum_{i = 1}^na_i,y = \sum_{i = 1}^na_i^2 x=i=1nai,y=i=1nai2

x − a 1 = ∑ i = 2 n a i − − − − − − − − − − − − − − − y − a 1 2 = ∑ i = 1 n a i 2 x - a_1 = \sum_{i = 2}^na_i \\ ---------------\\ y - a_1^2 = \sum_{i = 1}^na_i^2 \\ xa1=i=2naiya12=i=1nai2

有:
x − a 1 n ≤ y − a 1 2 n − − − − − − − − − − − − − − − ( x − a 1 ) 2 ≤ n ( y − a 1 2 ) − − − − − − − − − − − − − − − ( n + 1 ) a 1 2 − 2 x a 1 + x 2 − n y ≤ 0 \frac{x - a_1}{n} \leq \sqrt{\frac{y - a_1^2}{n}} \\ ---------------\\ (x - a_1)^2 \leq n(y - a_1^2) \\ ---------------\\ (n + 1)a_1^2 - 2xa_1 + x ^2 - ny \leq 0 \\ nxa1nya12 (xa1)2n(ya12)(n+1)a122xa1+x2ny0

先检验 Δ = b 2 − 4 a c ≥ 0 \Delta = b^2 - 4ac \geq 0 Δ=b24ac0

对于这个二次函数,用韦达定理
a 1 1 ⋅ a 1 2 = c a = x 2 − n y n + 1 a_{1_1} \cdot a_{1_2} = \frac{c}{a} = \frac{x^2 - ny}{n + 1} a11a12=ac=n+1x2ny

如果 n = 1 n = 1 n=1,构不成二次函数,请朴素的直接判断是否 y = x 2 y = x ^2 y=x2

AC-code:

#include<bits/stdc++.h>
using namespace std;
#define int long long		
int rd() {
	int x = 0, w = 1;
	char ch = 0;
	while (ch < '0' || ch > '9') {
		if (ch == '-') w = -1;
		ch = getchar();
	}
	while (ch >= '0' && ch <= '9') {
		x = x * 10 + (ch - '0');
		ch = getchar();
	}
	return x * w;
}

void wt(int x) {
	static int sta[35];
	int f = 1;
	if(x < 0) f = -1,x *= f;
	int top = 0;
	do {
		sta[top++] = x % 10, x /= 10;
	} while (x);
	if(f == -1) putchar('-');
	while (top) putchar(sta[--top] + 48);
}

void work(){
	int n = rd(),x = rd(),y = rd();
	if(n == 1) {
		if(y != (x * x)) puts("WA RE CE TLE MLE OLE");
		else printf("%.6lf\n",(double)(x * x) + 0.000000000001);
		return;
	}
	int b = -2 * x;
	int a = n;
	int c = x * x - (n - 1) * y;
	int delta = b * b - 4 * a * c;
	if(delta < 0) puts("WA RE CE TLE MLE OLE");
	else printf("%.6lf\n",(double)(x * x - (n - 1) * y) / (double)(n)+ 0.000000000001);
}

signed main() {
	
	int T = rd();
	while(T--) work();
	
	return 0;
}
  • 33
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值