校内模拟赛("扫地"杯 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) ans←i:1∼n∑sizi(n−sizi)
因为树上一共有 ( n 2 ) \dbinom{n}{2} (2n) 条路径,所以答案为 2 ans n ( n − 1 ) \frac{2\operatorname{ans}}{n(n - 1)} n(n−1)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 两边跑最短路计数:
如图:
我们发现,当一个点两个最短路计数乘积等于 s ⇝ t s \leadsto t s⇝t 最短路个数时,这个点在所有最短路上
本题跑 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=1nai1n≤ni=1∏nai≤n∑i=1nai≤n∑i=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 \\ x−a1=i=2∑nai−−−−−−−−−−−−−−−y−a12=i=1∑nai2
有:
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 \\
nx−a1≤ny−a12−−−−−−−−−−−−−−−(x−a1)2≤n(y−a12)−−−−−−−−−−−−−−−(n+1)a12−2xa1+x2−ny≤0
先检验 Δ = b 2 − 4 a c ≥ 0 \Delta = b^2 - 4ac \geq 0 Δ=b2−4ac≥0
对于这个二次函数,用韦达定理:
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}
a11⋅a12=ac=n+1x2−ny
如果 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;
}