[BZOJ 3553/3554/3562/3564/3565/3566][SHTSC/HBOI 2014]解题报告

Day1

Problem 1 信号增幅仪(BZOJ 3564)

http://www.lydsy.com:808/JudgeOnline/problem.php?id=3564

思路:如果裸着做会很麻烦,所以我们需要进行一个简单的预处理。和增幅仪的旋转角度相反,将所有点顺时针旋转a度,然后将所有点的横坐标缩小p倍。然后就可以套用最小圆覆盖的模板做了,这里我用的是随机增量法。

#include <iostream>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <algorithm>
#include <cmath>
 
#define PI 3.14159265358979323846
#define MAXN 510000
#define EPS (1E-20)
 
using namespace std;
 
struct Point
{
    double x,y;
    Point(double xx=0,double yy=0)
    {
        x=xx;
        y=yy;
    }
    Point operator-(Point a)
    {
        return Point(x-a.x,y-a.y);
    }
    Point operator+(Point a)
    {
        return Point(x+a.x,y+a.y);
    }
    double operator*(Point a)
    {
        return x*a.y-y*a.x;
    }
    Point operator*(double t)
    {
        return Point(x*t,y*t);
    }
    Point operator/(double t)
    {
        return Point(x/t,y/t);
    }
    Point rotate(double ang) //绕原点顺时针旋转ang度
    {
        return Point(x*cos(ang)+y*sin(ang),-x*sin(ang)+y*cos(ang));
    }
    Point verl()
    {
        return Point(-y,x);
    }
}dots[MAXN];
 
int n;
 
int dcmp(double x) //x>0返回1,x<0返回-1,x==0返回0
{
    if(fabs(x)<EPS) return 0;
    if(x>EPS) return 1;
    return -1;
}
 
double dist(Point a,Point b) //求点a到点b距离
{
    return sqrt((a.x-b.x)*(a.x-b.x)+(a.y-b.y)*(a.y-b.y));
}
 
Point cross(Point a,Point b,Point c,Point d) //求向量ab叉乘向量cd
{
    double s1=(d-a)*(c-a);
    double s2=(c-b)*(d-b);
    return (a*s2+b*s1)/(s1+s2);
}
 
Point cal(Point A,Point B,Point C)
{
    Point ret;
    double a1=B.x-A.x,b1=B.y-A.y,c1=(a1*a1+b1*b1)/2;
    double a2=C.x-A.x,b2=C.y-A.y,c2=(a2*a2+b2*b2)/2;
    double d=a1*b2-a2*b1;
    ret.x=A.x+(c1*b2-c2*b1)/d;
    ret.y=A.y+(a1*c2-a2*c1)/d;
    return ret;
}
 
int main()
{
    double ang,scale;
    scanf("%d",&n);
    for(int i=1;i<=n;i++)
        scanf("%lf%lf",&dots[i].x,&dots[i].y);
    scanf("%lf%lf",&ang,&scale);
    ang=ang/180*PI;
    for(int i=1;i<=n;i++)
    {
        dots[i]=dots[i].rotate(ang);
        dots[i].x/=scale;
    }
    double r=0; //r=三角形的外接圆半径
    Point c=dots[1];
    for(int i=2;i<=n;i++)
    {
        if(dist(dots[i],c)>r+EPS)
        {
            c=dots[i];
            r=0;
            for(int j=1;j<i;j++)
            {
                if(dist(dots[j],c)>r+EPS)
                {
                    c=(dots[i]+dots[j])/2;
                    r=dist(dots[j],c);
                    for(int k=1;k<j;k++)
                    {
                        if(dist(dots[k],c)>r+EPS)
                        {
                            c=cal(dots[i],dots[j],dots[k]);
                            r=dist(dots[i],c);
                        }
                    }
                }
            }
        }
    }
    printf("%.3lf\n",r);
    return 0;
}

Problem 2 概率充电器(BZOJ 3566)

http://www.lydsy.com:808/JudgeOnline/problem.php?id=3566

思路:树形DP,首先我们需要知道一个概率的公式,对于两个相互独立的事件A、B,P(A+B)=P(A)+P(B)-P(AB)。每个点都给它们的所有儿子贡献了一部分概率,第一次DFS时,我们需要求出每个点u的每个儿子v的充电概率P'(v)=P(v)+[P(u)*p(edge)]-[P(u)*p(edge)]*P(v)。由于第一次DFS时会混进重复计算的概率(因为父亲节点给本节点的概率不能被算进本节点给儿子节点的概率),所以要再进行一次DFS,去除掉重复的概率。

#include <iostream>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <algorithm>
#include <cmath>
 
#define MAXN 1001000
#define EPS (1E-6)
 
using namespace std;
 
struct edge
{
    int u,v,next;
    double p; //边的导电概率
}edges[MAXN];
 
int head[MAXN],nCount=0;
double pNode[MAXN]; //直接通电的概率
double f[MAXN];
double ans=0;
 
void AddEdge(int U,int V,double P)
{
    edges[++nCount].u=U;
    edges[nCount].v=V;
    edges[nCount].p=P;
    edges[nCount].next=head[U];
    head[U]=nCount;
}
 
void dfs1(int u,int fa) //从点u开始dfs,u的父亲是fa
{
    for(int p=head[u];p!=-1;p=edges[p].next)
    {
        int v=edges[p].v;
        if(v==fa) continue;
        dfs1(v,u);
        pNode[u]=pNode[u]+pNode[v]*edges[p].p-pNode[u]*edges[p].p*pNode[v];
    }
}
 
bool dcmp(double a)
{
    return fabs(a-0)<EPS;
}
 
void dfs2(int u,int fa)
{
    ans+=f[u];
    for(int p=head[u];p!=-1;p=edges[p].next)
    {
        int v=edges[p].v;
        if(v==fa) continue;
        double tmp=(1-pNode[v]*edges[p].p);
        if(dcmp(tmp)) //tmp==0
            f[v]=1.0;
        else
        {
            double y=(double)(f[u]-pNode[v]*edges[p].p)/(double)(1-pNode[v]*edges[p].p);
            f[v]=pNode[v]+y*edges[p].p-pNode[v]*y*edges[p].p;
        }
        dfs2(v,u);
    }
}
 
int main()
{
    int n;
    scanf("%d",&n);
    memset(head,-1,sizeof(head));
    for(int i=1;i<n;i++)
    {
        int u,v,p;
        scanf("%d%d%d",&u,&v,&p);
        AddEdge(u,v,(double)p/(double)100);
        AddEdge(v,u,(double)p/(double)100);
    }
    for(int i=1;i<=n;i++)
    {
        scanf("%lf",&pNode[i]);
        pNode[i]/=100;
    }
    dfs1(1,-1);
    f[1]=pNode[1];
    dfs2(1,-1);
    printf("%.6f\n",ans);
    return 0;
}

Problem 3 超能粒子炮(BZOJ 3565)

http://www.lydsy.com:808/JudgeOnline/problem.php?id=3565

Day2

Problem 1 神奇化合物(BZOJ 3562)

http://www.lydsy.com:808/JudgeOnline/problem.php?id=3562

Problem 2 三叉神经树(BZOJ 3553)

http://www.lydsy.com:808/JudgeOnline/problem.php?id=3553

Problem 3 神秘金字塔(BZOJ 3554)

http://www.lydsy.com:808/JudgeOnline/problem.php?id=3554



你可能感兴趣的:([BZOJ 3553/3554/3562/3564/3565/3566][SHTSC/HBOI 2014]解题报告)