2016.8.4信息学训练营练习

今天,是我们入营的第一天,进行了开营测试。已经一个月没有碰电脑的我,硬着头皮上了。。。。。。

T1:

音阶(ljestvica/1S/64M)

【题目描述】

Veronica进入了音乐学院。她收到了一张只有音符没有注释的乐谱,需要认出乐谱中用到的音阶。在本题中,我们只用到了两种最常用的(而且也是学校最先教的)音阶:A小调和C大调。这并不是说这两个音阶比其他大调、小调更简单或基础,所有的小调和大调都是差不多的。

 

现代音乐中一个八度有12个音(A, A#, B, C, C#, D, D#, E ,F, F#, G, G#),A小调和C大调也是用这12个音组成。A小调是一组有序的七个音{A,B,C,D,E,F,G},C大调是{C,D,E,F,G,A,B}。

 

注意,这两个音阶用到的音是一样的。那区别在哪?确定一个音阶,重点不仅在用到了什么音,还有他们的用法。主音(一个音阶的第一个音),                                                                                                                                下属音(第四个音),属音(第五个音)在一个音阶中是重音的首选。在A小调中就是A、D、E,在C大调中就是C、F、G。我们把这些音叫main tones。

 

大调和小调有什么不同呢?比方说,A小调的中音(第三个音)比主音高三个半音,C大调的中音比主音高四个半音。总之,差别就在于两个相邻的音的距离。这使小调听起来伤感,大调听起来喜庆。

 

现在你要写一个程序判断这首曲子是用A小调写的还是用C大调写的。可以数在重音(每小节的第一个音)中是A小调的main tones多还是C大调的main tones多。如果main tones数相同,若最后一个音是A小调的main tones,这首曲子就是A小调,否则就是C大调。

 

比如说,现在来判断著名的旋律“你在睡觉吗?”;

CD|EC|CD|EC|EF|G|EF|G|GAGF|EC|GAGF|EC|CG|C|CG|C

字符“|”把每个小节隔开了,所以这个旋律的重音依次是:C,E,C,E,E,G,E,G,G,E,G,E,C,C,C,C。有10个C大调的main tones,6个A小调的main tones,所以这个旋律是C大调的。

 

【输入格式】

输入文件仅一行, 包含一个序列(最短为5, 最长100),每个字母都包含在{“A”, “B”, “C”, “D”, “E”, “F”, “G”, “|”} 中。其中”|” 将每小节分开,且不会出现在序列的开头或结尾。

 

【输出格式】

输出文件仅一行,为” C-dur “(C大调) 或“ A-mol” (A小调)。

 

【输入1】

AEB|C

【输出1】

C-dur

【输入2】

CD|EC|CD|EC|EF|G|EF|G|GAGF|EC|GAGF|EC|CG|C|CG|C

【输出2】

C-dur

这题,是一道大水题,非常容易AC,不多说。

#include<iostream>
#include<string>
#include<cstdio>
using namespace std;
int main()
{
    freopen("ljestvica.in","r",stdin);
    freopen("ljestvica.out","w",stdout);
    int a=0,c=0;
    string st;
    cin>>st;
    if(st[0]=='A'||st[0]=='D'||st[0]=='E')
    {
        a++;
    }
    if(st[0]=='C'||st[0]=='F'||st[0]=='G')
    {
        c++;
    }
    for(int i=1;i<st.length();i++)
    {
        if(st[i]=='|')
        {
            int j=i+1;
            if(st[j]=='A'||st[j]=='D'||st[j]=='E')
            {
                a++;
            }
            if(st[j]=='C'||st[j]=='F'||st[j]=='G')
            {
                c++;
            }
        }
    }
    if(a>c)
    {
        cout<<"A-mol"<<endl;
    }
    if(c>a)
    {
        cout<<"C-dur"<<endl;
    }
    if(a==c)
    {
        if(st[st.length()-1]=='A'||st[st.length()-1]=='D'||st[st.length()-1]=='E')
        {
            cout<<"A-mol"<<endl;
        }
        else
        {
            cout<<"C-dur"<<endl;
        }
    }
    return 0;
}

T2;

波老师(teacher/1S/64M)

【题目描述】

波波老师是一个地理老师。有一天他上课的时候,他在地图上标记了N个点,第i个点在点(Xi,Yi)。他想知道,是否存在四个点(A,B,C,D)(A<B,C<D,AC或者BD),使AB之间的曼哈顿距离和CD之间的曼哈顿距离相等。

如果存在这样的四个点,输出YES,否则输出NO

 

【输入格式】

输入文件第一行是一个T(T50),表示有T组数据。

接下来有T组数据,每组数据第一行是两个整数N,M,表示点的个数以及点的坐标的边界,然后有N行,第i行有两个整数Xi,Yi表示第i个点的坐标(Xi,Yi)(0Xi,YiM)

 

【输出格式】

输出文件有T行,每一行为YES或者NO。

 

【输入】

2

3 10

1 1

2 2

3 3

4 10

8 8

2 3

3 3

4 4

 80% n<=1000,m<=100000

100% n<=100000,m<=100000

【输出】

YES

NO

这一题,80分很简单(只要不一边读入,一边break就行了,这样会使内容未读完而被误认为是下面的内容,所以,我们应该先读完再操作)

100分,只需要发现,在所有的路径中,最短是1,最长是200000,所以,最多只有200000种情况。当某种方案超过2000000种时,根据鸽巢原理(抽屉原理),其中一定有相同的。

#include<iostream>
#include<cstdio>
#include<cstdlib>
using namespace std;
struct hehe
{
    int x;
    int y;
};
hehe num[100001];
bool pd[200002];
int main()
{
    freopen("teacher.in","r",stdin);
    freopen("teacher.out","w",stdout);
    int t,n,m;
    cin>>t;
    for(int i=0;i<t;i++)
    {
        memset(num,0,sizeof(num));
        memset(pd,false,sizeof(pd));
        cin>>n>>m;
        bool ex=false;
        for(int i=0;i<n;i++)
        {
            cin>>num[i].x>>num[i].y;
        }
        if((n)*(n+1)/2>m)
        {
            cout<<"YES"<<endl;
            continue;
        }//鸽巢原理
        for(int i=0;i<n;i++)
        {
            for(int j=0;j<i;j++)
            {
                if(num[i].x<=m&&num[i].y<=m&&num[j].x<=m&&num[j].y<=m)
                {
                if(pd[abs(num[i].x-num[j].x)+abs(num[i].y-num[j].y)])
                {
                    ex=true;
                    break;
                }
                else
                {
                    pd[abs(num[i].x-num[j].x)+abs(num[i].y-num[j].y)]=true;
                }                    
                }
            }
        }
        if(!ex)
        cout<<"NO"<<endl;
        else
        cout<<"YES"<<endl;
    }
    return 0;
}

爆裂吧世界(world/1S/64M)

 

【题目描述】

给你一个长度为n的数列A,请你计算里面有多少个四元组(a,b,c,d)满足:
a
bcd,1a<bn,1c<dn,Aa<Ab,Ac>Ad

 

【输入格式】

输入文件第一行有一个整数N,第二行有N个整数A1,A2An

 

【输出格式】

输出文件仅一行,为一个整数,表示满足条件的四元组的数量

 

【输入1】

4

2 4 1 3

【输出1】

1

【输入2】

4

1 2 3 4

【输出2】

0

【数据约定】

15% n<=100   

100%n<=50000

A在int范围里

这题,难度是有的,但是不大,在做的时候不够时间。

简单来说,就是树状数组+容斥原理。

用树状数组用求出逆序对的方法求出每个数当b,c有多少种(两个树状数组)。

然后我们发现,b+d=这个数前面的数-1.因为前面的数不是比他大就是比他小。

a+c=这个数后面的数-1,理由同上。

然后,算出一共有多少种组合。

再用总数-(某个数同时做a,c.a,d.b,c.b,d).因为只有这四种。

就得出了最后的答案。

#include<iostream>
#include<cstdio>
#include<algorithm>
using namespace std;
int sum[50005];
int ansb[50005],ansc[50005],ansa[50005],ansd[50005];
int n;
int query(int x)
{            
    int ans1=0;            //计算1-x的和
    for (ans1 = 0;x>0;x -= (x & (-x)))//求x变成二进制后第一个1的位置
        ans1+=sum[x];
    return ans1;
}
int change(int x,int delta){//改变第x个数,让他加上delta
    for ( ;x<=n;x += (x & (-x)))
        sum[x] += delta;
}
struct hehe
{
    int m;
    int op;
    int np;
};
hehe num[50001];
int cmp(hehe x,hehe y)
{
    return x.m<y.m;
}
int cmp1(hehe x,hehe y)
{
    return x.op<y.op;
}
int main()
{
    freopen("world.in","r",stdin);
    freopen("world.out","w",stdout);
    long long tot=0,tota=0,totc=0;
    cin>>n;
    for(int i=1;i<=n;i++)
    {
        cin>>num[i].m;
        num[i].op=i;
    }    
    sort(num+1,num+n+1,cmp);
    int i1=0;
    for(int i=1;i<=n;i++)
    {
        if((i==1)||(num[i].m!=num[i-1].m)) i1++;//防止两数相等
        num[i].np=i1;
    }
    sort(num+1,num+n+1,cmp1);
    for(int i=1;i<=n;i++)
    {
        ansb[i]=query(num[i].np-1);//求b
        ansd[i]=i-1-query(num[i].np);//注意这里是没有减一的
        change(num[i].np,1);
    }
    memset(sum,0,sizeof(sum));
    for(int i=n;i>=1;i--)
    {
        ansc[i]=query(num[i].np-1);
        ansa[i]=n-i-query(num[i].np);
        change(num[i].np,1);
    }
    for(int i=1;i<=n;i++)
    {
        tota+=ansa[i];
        totc+=ansc[i];
    }
    tot=tota*totc;
    for(int i=1;i<=n;i++)
    {
        tot-=(long long)ansa[i]*ansc[i];
        tot-=(long long)ansa[i]*ansd[i];
        tot-=(long long)ansb[i]*ansc[i];
        tot-=(long long)ansb[i]*ansd[i];
    }
    cout<<tot<<endl;
    return 0;
}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值