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

本文介绍了如何从无根树构建有根二叉树,并讨论了距离统计问题的解决策略。在二叉树问题中,判断节点是否能作为根节点的关键是其连边数。在距离统计部分,当线段长度为勾股数时,需要考虑斜向放置的情况,通过优化算法避免超时。最后,文章探讨了电阻网络问题,提出了利用栈处理串联和并联电阻的方法。
摘要由CSDN通过智能技术生成

一、二叉树的根

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

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

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

依题目所说,要构成二叉树,一个点连出去的边最多为3条(两个后继,一个祖先),据此可判断无解情况;而什么时候一个节点能作根呢?我们知道一个二叉树的根最多有2个后继,因而有3条连边的不可作根,其余可以。那么就可以记录每个节点的连边数,按上述分析来统计。

二、距离统计

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

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

这题显而易见的是,当知道线段长,在纵向与横向上,我们可以用乘法原理来求解(n-l*m+m-l*n),关键在于若该长度为勾股数,那么他可以斜着安置,如图:

(3*3+4*4=5*5 长度为5时)

暴力的话可用o(根号l)枚举,但l<=1000000000000000000,(n*m),故超时。

此处转载题解:

注意:经研究发现,由于z-y为完全平方数,z-x为完全平方数2倍(y与x可互换)同时存在的(看题解最后一点证明),因而只枚举一个即可,然后得出该斜向线段覆盖的矩阵长宽,就可用((n-a)*(m-b)+(n-b)*(m-a))求解,还需判断该点阵能否容纳(⊙o⊙)哦。见程序:

01

#include<iostream>

 

02

#include<cstdio>

 

03

#include<algorithm>

 

04

#include<cmath>

 

05

using namespace std;

 

06

int t,x,y,x1;

 

07

long long ans,n,m,a,b;

 

08

long long fan(int x){

 

09

    long long y=x;

 

10

    return(y*y);

 

11

}

 

12

void did(long long x,long long y){

 

13

    if ((n>x)&&(m>y))ans+=(n-x)*(m-y)*2;

 

14

    return;

 

15

}

 

16

int gcd(int x,int y){

 

17

    if (x==0)return(y);

 

18

    return(gcd(y%x,x));  

 

19

}

 

20

void work(int x,int bz){

 

21

     static long long fa;

 

22

     static int a;

 

23

     static int b;

 

24

     fa=fan(x);

 

25

     for (int i=1;i<=sqrt(x);i++)

 

26

        if (fan(i)!=x){

 

27

         a=x-fan(i);

 

28

         b=sqrt(fan(x)-fan(a));

 

29

         if (fan(a)+fan(b)==fa)

 

30

            if ((gcd(a,b)==1)&&(gcd(a,x)==1)&&(gcd(b,x)==1))did(a*bz,b*bz);

 

31

         a=x-fan(i)*2;

 

32

         if (a>0){

 

33

         b=sqrt(fan(x)-fan(a));

 

34

         if (fan(a)+fan(b)==fa)

 

35

            if ((gcd(a,b)==1)&&(gcd(a,x)==1)&&(gcd(b,x)==1))did(a*bz,b*bz);

 

36

        }

 

37

     }

 

38

}

 

39

int main(){

 

40

    scanf("%d%d%d",&n,&m,&t);

 

41

    for (int i=1;i<=t;i++){

 

42

        scanf("%d",&x);

 

43

        ans=0;

 

44

        if (i==114){

 

45

            ans=0;

 

46

        }

 

47

        if (n>=x)ans+=(n-x)*m;

 

48

        if (m>=x)ans+=(m-x)*n;

 

49

        for (int j=1;j<=sqrt(x);j++)

 

50

            if (x%j==0){

 

51

               x1=x/j;

 

52

               work(x1,j);

 

53

               if (x1!=j){

 

54

               x1=j;

 

55

               work(x1,x/j);

 

56

            }              

 

57

            }

 

58

        printf("%lld ",ans);

 

59

    }

 

60

    return 0;

 

61

}

 

三、电阻网络

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

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

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

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

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

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

理解了电路后,我们可将连接形式分为并联、串联和混联(串+并),而串联电路电阻为:R总=U/I总=R1+R2+R3+……,并联电阻为1/R总=1/R1+1/R2+1/R3+…

对于串联电路,我们要求出每个用电器(电阻)的电阻,而其中出现了并联,则将整个并联看做一个电阻,优先处理并联电路的电阻才能处理串联的;对于并联电路,我们要求出每个支路的电阻,而其中出现并联,由于是R总=U/I总=R1+R2+R3+……,可以直接加过去,而遇到并联电路,则将整个并联看做一个电阻,优先处理并联电路的电阻才能处理外接的并联的。

       由此,我们可以发现,当我们遇到串联时,可直接加过去,而并联时,要优先处理,形成一个优先级别,可以用栈进行处理,这样就可以的出解法:

       遇到串联时,直接加过去,遇到并联起点,新开一个栈,遇到并联终点时,将各支路电阻倒数求和再倒数,由于具有传递性和单向性,我们可以将电阻值储存在接点上,一个个点转移,见程序:

01

#include<cstdio>

 

02

#include<algorithm>

 

03

#include<iostream>

 

04

#include<iomanip>

 

05

using namespace std;

 

06

int n,m,l,r,sum;

 

07

double a[100001],h[100001];

 

08

double q;

 

09

int e[500001][3],g[100001],b[100001],c[100001],d[100001],f[100001];

 

10

void in(int x,int y,int z){

 

11

    e[++sum][0]=y;

 

12

    e[sum][1]=z;

 

13

    e[sum][2]=g[x];

 

14

    g[x]=sum;

 

15

}

 

16

void work(){

 

17

    int x;

 

18

    if (c[d[r]]>1)h[++sum]=a[d[r]];

 

19

    while (!((g[d[1]]==0)&&(r==1))){

 

20

        if (g[d[r]]!=0){

 

21

                x=g[d[r]];

 

22

                if (b[e[x][0]]>1){

 

23

                   q=a[d[r]]+e[x][1];

 

24

                   if (q!=0)

 

25

                   a[e[x][0]]+=1/q;

 

26

                   }else {if (e[x][1]==1)a[e[x][0]]++;

 

27

                         a[e[x][0]]+=a[d[r]];}

 

28

                g[d[r]]=e[x][2];

 

29

                d[++r]=e[x][0];

 

30

                f[d[r]]++;

 

31

                if (f[d[r]]==b[d[r]]){

 

32

                if ((b[d[r]]==f[d[r]])&&(b[d[r]]>1)){

 

33

                    if (a[d[r]]!=0)a[d[r]]=1/a[d[r]]+h[sum];else a[d[r]]=h[sum];

 

34

                    sum--;

 

35

                }

 

36

                if (c[d[r]]>1){h[++sum]=a[d[r]];a[d[r]]=0;}

 

37

                }else r--;

 

38

        }else r--;

 

39

    }

 

40

}

 

41

int main(){

 

42

    scanf("%d %d",&n,&m);

 

43

    int x,y,z;

 

44

    for (int i=1;i<=m;i++){

 

45

        scanf("%d %d %d",&x,&y,&z);

 

46

        in(x,y,z);

 

47

        b[y]++;c[x]++;

 

48

    }

 

49

    sum=0;

 

50

    for (int i=1;i<=n;i++)

 

51

        if (b[i]==0){

 

52

            r++;

 

53

            d[r]=i;

 

54

            break;

 

55

        }

 

56

    work();

 

57

    printf("%.3f",a[n]);

 

58

    return 0;

 

59

}

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值