CH Round #49 - Streaming #4 (NOIP模拟赛Day2)

该博客主要介绍了NOIP模拟赛Day2中的三个问题:如何在无根树中寻找符合条件的二叉树根,距离统计问题的解决策略,以及电阻网络的电路电阻计算方法。通过对样例的分析,阐述了解题思路和数据范围约束。

二叉树的根 CH Round #49 - Streaming #4 (NOIP模拟赛Day2)

描述

什么叫二叉树?这个应该是每个人都知道的。在本题中,我们的二叉树是有根的:对于每个节点,它至多有左右两个子节点(可以为空),并且每个子树也都是二叉树。

如果无视有根二叉树中的父子关系,那么它就变成了无根树。现在我们给出任意的无根树,想让你找到这样的节点:存在某个有根二叉树的根为这个节点,且无视有根二叉树中的父子关系后,所变成的无根树恰好为给出的无根树。

由于有可能无解,也有可能有很多解,你需要对这些情况作出判断。

输入格式

第一行为一个正整数n,代表树中点的个数。

接下来n-1行,每行一对非负整数a[i],b[i],表示a[i]与b[i]之间连有一条边。

 

输出格式

第一行为一个非负整数,表示解的数量。

如果解的数量不为0,那么第二行有若干正整数,每个表示一个答案,且按升序排列。

样例输入
输入1:
3
1 2
2 3
输入2:
4
1 2
1 3
1 4
样例输出
输出1:
3
1 2 3
输出2:
3
2 3 4
数据范围与约定

对于20%的数据,n≤3。

对于40%的数据,n≤4。

对于60%的数据,n≤100。

对于80%的数据,n≤1,000。

对于100%的数据,n≤100,000。

样例解释

对于第一个样例,任意一个节点都可以成为根,使得树成为二叉树。

对于第二个样例,如果以节点1为根,那么它就会有3个子节点,破坏了二叉树性质。而对于其余节点都是可行的。


Solution : 稍微思考一下可以发现,如果无根树中存在出度和入度之和大于3的点的话,那么整一个无根树无论取谁为根都是是无法满足题目要求的,而满足要求的点则满足出度和入度之和小于3。


距离统计 CH Round #49 - Streaming #4 (NOIP模拟赛Day2)

描述

现在有一个n×m的点阵,以这些点为顶点可以组成很多条线段。

有T次询问,每次询问给出l,求这些线段中长度为l的个数。

输入格式

第一行为三个正整数n,m,T,分别代表点阵的长和宽,以及询问的次数。

第二行为T个正整数,每个表示一个l。

输出格式

一行T个正整数,每个表示一个答案。

样例输入
输入1:
3 3 3
1 2 3
输入2:
4 5 1
5
样例输出
输出1:
12 6 0
输出2:
2
数据范围与约定

对于20%的数据,n,m≤20,T≤10。

对于40%的数据,n,m≤1,000,T≤100。

对于60%的数据,n,m≤100,000。

对于100%的数据,n,m≤1,000,000,000,T≤1,000,l是在[1,2max(n,m)]内随机生成的。

样例解释

对于第一个样例,长度为1的线段有12条(横竖各6条),长度为2的线段有6条(横竖各3条),没有长度为3的线段。

对于第二个样例,长度为5的线段只有2条对角线。

Solution : 因为问题的主要是要求a^2 + b^2 = l^2,我们有一个结论,如果x^2 + y^2 = z^2 && gcd(x,y,z) == 1那么z-x是一个完全平方数,或者一个完全平方数的两倍。于是我们可以通过枚举l的约数,使得问题得到转化。

#include<cstdio>
#include<cmath>
#define Push 0.1
#define lim 50000
#define ll long long
using namespace std;
bool prime[lim+10];
int num[lim],tot,ask[1100],how,l,a[lim],x[lim];
long long n,m,t,ans;
void init(){
	scanf("%lld%lld%lld",&n,&m,&t);
	for (int i = 1;i <= t;i ++) scanf("%d",&ask[i]);
}

void Select_Prime(){
	for (int i = 2;i <= lim;i ++) if (! prime[i]){
		num[++tot] = i;
		for (int j = i*2;j <= lim;j += i) prime[i] = true;
	}
}

void calc(int l){
	how = 0;
	for (int i = 1;i <= tot;i ++){
		if (num[i] * num[i] > l) break;
		if (l % num[i] == 0){
			x[++ how] = num[i];
			a[how] = 0;
			while (l % num[i] == 0){
				l /= num[i];
				a[how] ++;
			}
		}
	}
	if (l > 1){
		x[++ how] = l;
		a[how] = 1;
	}
}

long long gcd(long long a,long long b){
	long long c;
	while (b){
		c = a % b; a = b;b = c;
	}
	return a;
}

void Plus(long long a,long long b){
	if (n > a && m > b) ans += (n-a)*(m-b)*2;
	if (m > a && n > b) ans += (m-a)*(n-b)*2;
}

void check(int x){
	for (int dd = 1;dd <= 2;dd ++){
		for (int i = 1;i <= x;i ++){
			long long b = x - i*i*dd;
			if (b <= 0) break;
			long long gue = (ll)x*(ll)x-b*b;
			double g = sqrt(gue);
			if (g >= b) break;
			long long a = g + Push;
			if (a*a == gue && gcd(x,gcd(a,b)) == 1) Plus(a*(l/x),b*(l/x));
		}
	}
}

void inumerate(int wh,long long sum){
	if (wh > how) check(sum);else
	for (int i = 1;i <= a[wh]+1;i ++){
		inumerate(wh+1,sum);
		sum *= x[wh];
	}
}

void work(){
	Select_Prime();
	for (int i = 1;i <= t;i ++){
		l = ask[i];
		ans = 0;
		if (l < m) ans += (m-l) * n;
		if (l < n) ans += (n-l) * m;
		calc(l);
		inumerate(1,1);
		printf("%lld ",ans);
	}
}

int main(){
	init();
	work();
}

电阻网络 CH Round #49 - Streaming #4 (NOIP模拟赛Day2)

描述

什么是电阻?这个大家应该都知道。什么是电路?大家也应该知道。但是本题当中,电路的定义或许有点不同:

电路都带有正、负极接点,正极在左,负极在右。具体地:电路分为以下几类:

单独的一个1Ω电阻(及其两端的接点)是电路。(虽然导线也可以被视为0Ω的电阻,但是单独的导线不是电路

如果A和B都是电路,设1,2,3是从左到右的三个接点,那么将A的正负极分别接在1与2上,将B的正负极分别接在2与3上,那么1到3的部分是电路,其中1为正极,3为负极。

如果A和B都是电路,设1,2,3,2',3',1'是六个接点,其中1在2和3的左侧,2在2'的左侧,3在3'的左侧,2'和3'在1'的左侧,并且1与2,1与3,2'与1',3'与1'间均连有导线,那么将A的正负极分别接在2与2'上,将B的正负极分别接在3与3'上,那么1到1'的部分是电路,其中1为正极,1'为负极。

现在给出一个电路,求它正负极之间的电阻。

输入格式

第一行为两个正整数n,m,分别代表接点数和电阻数。保证编号小的接点在编号大的接点的左侧。

接下来m行,每行三个整数a[i],b[i],c[i],代表这个电阻连接了a[i]与b[i]接点,其阻值为c[i],其中c[i]只可能是0或1,且对于任意的i,保证a[i]<b[i]。

输出格式

输出一个实数,表示总的电阻值,保留三位小数输出。

样例输入
7 7
1 2 0
1 3 0
2 4 1
3 5 1
4 6 0
5 6 0
6 7 1
样例输出
1.500
数据范围与约定

对于20%的数据,n≤5。

对于50%的数据,n≤100。

对于70%的数据,n≤1,000。

对于100%的数据,n≤100,000,数据是在人工指定的$n$下随机生成的,保证答案不会超过10.000。

样例解释

画出图来,答案是显然的。

Solution : 这其实只是一道模拟题,考的是题目阅读能力(大雾),对于一个电路,我们从1号接线柱开始搜索,用vector记录一个点的出路,如果是并联的电路就当作两个串联处理,只是最后要用并联的公式处理答案而已,这样一直搜索到最后一个接线柱就可以得出答案了。

#include<cstdio>
#include<vector>
#define maxn 100100
using namespace std;
double ans;
std::vector<int> son[maxn];
int n,m,u,v,x,t,resi[maxn];
void init(){
	scanf("%d%d",&n,&m);
	for (int i = 1;i <= m;i ++){
		scanf("%d%d%d",&u,&v,&x);
		son[u].push_back(v);
		resi[u] = x;
	}
}

double dfs(int x,int &t){
	if (x == n) return 0;
	if (resi[x] == 0){
		if (son[x].size() == 1){
			t = son[x][0];
			return 0;
		} else {
			double a = dfs(son[x][0],t);
			double b = dfs(son[x][1],t);
			return a*b/(a+b) + dfs(t,t);
		}
	} else return dfs(son[x][0],t)+1;
}

int main(){
	init();
	ans = dfs(1,t);
	printf("%.3lf",ans);
}


后记:这场比赛其实真正说没想到的只有第二题,但是当时看完题目后认为第二题会比第三题更加好写,于是就先做第二题,花了一个小时之后,越陷越深,到最后还是没有想出正解,结果白白浪费了第三题,以后对于一些题目意思较难明白的题目不要认为就是难做,很可能是道水题,而对于已经开始想的题,如果没有十分充足的把握,就应该先放一边。

评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值