7.30个人排位赛08赛后题解

A.游戏


判断凸多边形有几条边的两角有钝角。

总结:1.计算几何中尽量运用点乘和叉乘运算   点乘:x1*x2+y1*y2   叉乘:x1*y2-x2*y1

    2.注意区间两端的处理

            3.判断浮点数正负的优美写法     return x<-eps?-1:x>eps     eps:足够小,视精度要求而定

代码:

#include<iostream>
#include<cstdio>
#include<cstdlib>
#include<algorithm>
#include<cmath>
#define eps 0.000000001
using namespace std;
struct point
{
    double x,y;
};
int cmp(double x)
{
    return x<-eps?-1:x>eps;
}
int alm(point a,point b,point c)
{
    return cmp((a.x-b.x)*(a.x-c.x)+(a.y-b.y)*(a.y-c.y));
}
int main()
{
    int n,i,j;
    point p[1010];
    int c[1010];
    while(scanf("%d",&n)!=EOF)
    {
        for(i=0;i<n;i++)
        {
            scanf("%lf %lf",&p[i].x,&p[i].y);
        }
        for(i=1;i<n-1;i++)
        {
            c[i]=alm(p[i],p[i-1],p[i+1]);
        }
        c[0]=alm(p[0],p[1],p[n-1]);
        c[n-1]=alm(p[n-1],p[n-2],p[0]);
        int cnt=0;
        if(c[0]<0||c[n-1]<0)
            cnt++;
        for(i=1;i<n;i++)
        {
            if(c[i]<0||c[i-1]<0)
                cnt++;
        }
        printf("%d\n",cnt);
    }
    return 0;
}

B.小妹妹送快递

时间限制 1000 ms  内存限制 65536 KB

题目描述

Mays王国的女王大人每天过着自由自在的生活,她最大的乐趣就是给邻国的帅气王子写信。但是最近,Mays王国的叔叔们变得很无聊,他们知道女王大人每次都把信委托给皇家小妹妹快递公司的小妹妹们,于是叔叔们给每一条路都设立了路障,只有小妹妹们给他们表演节目才会让小妹妹们过去。
在每一个路障,都有不同数量的叔叔,只有表演的小妹妹的数量不少与叔叔的数量的时候叔叔才会放她们过去。
为了节省开销,小妹妹快递公司希望派最少的小妹妹把女王大人的信件送到。请你告诉他们需要派几个小妹妹。

输入格式

输入第一行为数据组数T(T<=10),接下来T组数据,每组第一行为n,m,,2<=n<=10000,1<=m<=100000,表示Mays王国的道路由n个节点组成,接下来m行,每行一组u,v,c表示连接节点u,v的一条无向道路,且路障上有c个叔叔,1<=u,v<=n,0<=c<=100。女王大人和皇家小妹妹快递公司都在节点1,帅气的邻国王子住在节点n。

输出格式

每组数据输出一个数字,表示小妹妹快递公司最少需要派出的小妹妹数量。如果无论派出多少小妹妹都无法把信送到帅气的邻国王子手里,输出"shimatta!"。

输入样例

1
3 3
1 2 1
2 3 1
1 3 3

输出样例

1
题意分析:要求从1走到n所有路径中线路代价最大值的最小值

用并查集做,先根据代价从小到大对边排序,然后依次加入并查集,直到1与n联通。

但注意:实际意义,若最终求得值为0,则至少要派一位小妹妹去。

代码:

#include<iostream>
#include<cstdio>
#include<cstdlib>
#include<cmath>
#include<cstring>
#include<algorithm>
#include<queue>
#include<stack>
using namespace std;
struct edge
{
    int from,to,cost;
    bool operator < (const edge b) const
    {
        return cost<b.cost;
    }
};
int father[10010],ranks[10010];
edge e[100010];
int finds(int x)
{
    if(father[x]==x)
        return x;
    else
        return father[x]=finds(father[x]);
}
void unions(int x,int y)
{
    x=finds(x);
    y=finds(y);
    if(x==y)
        return ;
    else if(ranks[x]<ranks[y])
        father[x]=y;
    else
    {
        father[y]=x;
        if(ranks[x]==ranks[y])
            ranks[x]++;
    }
}
int main()
{
    int t,n,m,i;
    scanf("%d",&t);
    while(t--)
    {
        scanf("%d %d",&n,&m);
        memset(ranks,0,sizeof(ranks));
        for(i=0;i<=n;i++)
            father[i]=i;
        for(i=0;i<m;i++)
        {
            scanf("%d %d %d",&e[i].from,&e[i].to,&e[i].cost);
        }
        sort(e,e+m);
        for(i=0;i<m;i++)
        {
            unions(e[i].from,e[i].to);
            if(finds(n)==finds(1))
                break;
        }
        if(finds(n)==finds(1))
        {
            if(e[i].cost!=0)
                printf("%d\n",e[i].cost);
            else
                printf("1\n");
        }          
        else
            printf("shimatta!\n");
    }
    return 0;
}

C.学姐点名

题意:1~n中给出n-1个数,找出哪个数没有出现

2组数据N为1000000

500组数据满足N不大于3000

1000组数据满足N不大于10

注意:1-n的和再减去出现的数字,就得到该数。(或者用异或运算)

long long sum; int n;

sum=n*(n+1)/2; 会出错。因为最后计算等号,n*(n+1)始终属于int型运算,会爆掉。(可以直接用循环从1加到n)

代码:

#include<iostream>
#include<cstdio>
#include<cstdlib>
using namespace std;
int main()
{
    long long sum,n,a;
    while(scanf("%lld",&n)!=EOF)
    {
        sum=n*(n+1)/2;
        n--;
        while(n--)
        {
           scanf("%lld",&a);
           sum-=a;
        }
        printf("%lld\n",sum);
    }
    return 0;
}

时间限制 1000 ms  内存限制 65536 KB

题目描述

明光村迎来了一年一度的盛世——解码锦标赛,有 2^N 次个队伍从世界各村赶来参与比赛,编号为 1 - 2^N。赛制为每一轮晋级一半队伍,按序号大小两两比赛,淘汰弱者。一轮结束后,所有的胜者进入下一轮,依旧是按顺序两两比赛。比如第一轮就是 1 vs 2, 3 vs 4 ... 2^N - 1 vs 2^N。在一旁围观的 Mays 学姐告诉你,N次比赛后的胜者是唯一的。现在你拿到了一份各个参赛队伍的对抗胜率表 win,为 2^N * 2^N 的矩阵, win[i][j] 为一位小数,代表i胜j的概率。 

你能告诉 Mays 学姐最有可能获得世界冠军的是那支队伍吗?

输入格式

多组数据。每组第一行为 N ,N <= 8,接下来 N 行 N 列为对抗胜率矩阵。 保证 win[i][j] + win[j][i] = 1 (i != j)。 以 N=0 结束输入。 

输出格式

对每组数据,输出胜率最大的队伍的序号。如果最大的两个概率相差不到 0.001,则认为胜率相等,输出序号最小者。

输入样例

2
0.0 0.1 0.2 0.3
0.9 0.0 0.4 0.5
0.8 0.6 0.0 0.6
0.7 0.5 0.4 0.0
2
0.0 0.8 0.1 0.4 
0.2 0.0 0.2 0.6 
0.9 0.8 0.0 0.3 
0.6 0.4 0.7 0.0 
0

输出样例

2
4
主要是找到胜率的计算方法

win[ i ][ j ]表示 j 在第 i 局赢得概率

win[ i ] [ j ]=win[ i-1 ] [ j ] * (求和 a[ j ][ k ] * win [i-1] [ k ],k为j可能遇到的对手)

第一次交的时候少了#include<cstring> 用了memset,然后就ce了.....

代码:

#include<iostream>
#include<cstdio>
#include<cstdlib>
#include<cmath>
#include<cstring>
#include<algorithm>
#define eps 0.001
using namespace std;
int cmp(double x)
{
    return x<-eps?-1:x>eps;
}
int main()
{
    int n,i,j,p,jushu,pos[300];
    double a[300][300],win[10][300];
    int power[9]={1,2,4,8,16,32,64,128,256};
    while(scanf("%d",&n)&&n!=0)
    {
        memset(win,0,sizeof(win));
        for(i=0;i<power[n];i++)
        {
            pos[i]=i;
            win[0][i]=1;
            for(j=0;j<power[n];j++)
                scanf("%lf",&a[i][j]);
        }
        for(jushu=1;jushu<=n;jushu++)//第I局
        {
            for(i=0;i<power[n];i++)
            {
                if(pos[i]%2==0)
                {
                    for(j=(pos[i]+1)*power[jushu-1];j<(pos[i]+2)*power[jushu-1];j++)
                    {
                        win[jushu][i]+=a[i][j]*win[jushu-1][j];
                    }
                    win[jushu][i]*=win[jushu-1][i];
                }
                else
                {
                     for(j=(pos[i]-1)*power[jushu-1];j<pos[i]*power[jushu-1];j++)
                    {
                        win[jushu][i]+=a[i][j]*win[jushu-1][j];
                    }
                    win[jushu][i]*=win[jushu-1][i];
                }
            }
            for(i=0;i<power[n];i++)
                pos[i]/=2;
        }
        double maxn=0;
        int id;
        for(i=0;i<power[n];i++)
        {
            if(win[n][i]-maxn>0.001)
            {
               maxn=win[n][i];
               id=i;
            }
        }
        printf("%d\n",id+1);
    }
    return 0;
}

时间限制 10000 ms  内存限制 65536 KB

题目描述

焦级长和田田玩算数。焦级长给田田一个数列ai,然后问田田第i项到第j项的数的和。
田田随手写了个简单的数据结构就搞定了。于是焦级长打算加大题目难度。
焦级长又添加了一个操作,即从第i项到第j项都按顺序加上一个等差数列的项
如从第2项到第4项加入一个首项为1,公差为3的等差数列即第2项+1,第3项+4,第4项+7,以此类推
于是田田写了半天又WA又T还RE了一炮,终于来求救你让你来写个程序帮他

 

输入格式

第一行为case数T
每个case第一行输入n,m,表示有数列长为n,有m个操作
之后n个数为ai即数列的初始数
之后m行为m个操作
第一个操作为插入
为1,l,r,x,d
1表示插入操作,l,r为数列的[l,r]会进行操作,等差数列的首项为x,公差为d
第二个操作为查询和
为2,l,r
2表示查询,l,r为求和的范围,即求al到ar的数列的和

T<=10
n,m<=10^5
ai,x<10^5,|d|<=100;

输出格式

每个查询输出一个值

输入样例

1

6 9
1 2 3 4 5 6
2 1 4
2 1 6
1 1 4 1 0
2 2 5
1 2 4 1 1
2 1 4
2 2 4
2 1 5
2 2 6

输出样例

10
21
17
20
18
25
29
线段树...待我学习一下在写

你可能感兴趣的:(并查集,概率)