2016.8.4 开营测试总结

本文解析了三道算法竞赛题目,包括字符串分析、点集的曼哈顿距离匹配问题及数列中特定四元组计数问题。通过直接模拟、暴力搜索和树状数组等方法给出了详细的解答过程。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

第一题:
题意:字符串用”|”分隔为一个个小结,统计每一小节第一个字母分别属于{ADE}和{CFG}的次x1、x2。
若x1>x2,输出A-mol
若x2>x1输出C-dur
若x1=x2 则用字符串最后一个字母属性来决定。

分析:直接模拟。

程序:

#include <cstdio>
#include <cstring>

int main()
{
    freopen("ljestvica.in","r",stdin);
    freopen("ljestvica.out","w",stdout);
    char s[1000],t[1000];
    scanf("%s",&s);
    int move=0,Am=0,Cd=0;
    int L;
    s[strlen(s)]='|';
    for (int i=0;i<strlen(s);i++)
    {
        if (s[i]=='|') {
            if (t[0]=='A'||t[0]=='D'||t[0]=='E') Am++;
            if (t[0]=='C'||t[0]=='F'||t[0]=='G') Cd++;
            L=move-1; move=0;
        } else {
            t[move++]=s[i];
        }
    }
    if (Am>Cd||(Am==Cd&&(t[L]=='A'||t[L]=='D'||t[L]=='E'))) printf("A-mol\n");
        else printf("C-dur\n");
    return 0;
}

第二题:
题意:给出N个点(Xi,Yi)。求是否存在四个点(A,B,C,D)(A< B,C< D,A≠C或者B≠D),使AB之间的曼哈顿距离和CD之间的曼哈顿距离相等。(0< Xi,Yi< =10^5)

分析:注意曼哈顿距离就是|Xi-Xj|+|Yi-Yj|。
暴力:用O(N^2)的方法求出任意两点的距离L (L<2*10^5),并以其为下标记录。若有重复标记则存在,否则不存在。
这种方法不会超时,因为点之间可能的曼哈顿距离种数最多只有2*10^5。(容斥原理)

程序:

#include <cstdio>
#include <cstring>
#include <cmath>
#include <algorithm>
using namespace std;
const int maxn=500000;
struct Point
{
    int x,y;
}dots[maxn];
int n,m;
bool cmp(const Point &a,const Point &b)  //点的排序 
{
    return (a.x<b.x||a.x==b.x&&a.y<b.y);
}
int dist(const Point a,const Point b)   //求曼哈顿距离 
{
    return abs(a.x-b.x)+abs(a.y-b.y);
}
int main()
{
    freopen("teacher.in","r",stdin);
    freopen("teacher.out","w",stdout);
    int T; scanf("%d",&T);
    bool a[maxn*2];
    while (T--)
    {
        scanf("%d%d",&n,&m);
        for (int i=0;i<n;i++) scanf("%d%d",&dots[i].x,&dots[i].y);
        sort(dots,dots+n,cmp); int Len=-1;
        memset(a,false,sizeof(a)); bool flag=false;
        for (int i=0;i<n-1;i++)
        {
            for (int j=i+1;j<n;j++) {
                int k=dist(dots[i],dots[j]);
                if (a[k]) { flag=true; break; } else a[k]=true;
                }
            if (flag) { printf("YES\n"); break; }
        }
        if (!flag) printf("NO\n");
    }
    return 0;
}

第三题:
题意:一个长度为n的数列A,计算里面有多少个四元组(a,b,c,d)满足:
a≠b≠c≠d,1≤a< b≤n,1≤c< d≤n,Aa< Ab,Ac>Ad

分析:
求出每一个数左右两边比自己小或大的个数(树状数组),即合法的(a,b)组合个数和(c,d)组合个数。总数之积则为合法四元组(a,b,c,d)的个数。(乘法原理)
因为a≠b≠c≠d,所以就要去掉a=b(不可能),c=d(不可能),a=c,b=c,a=d,b=d几种情况。Small为比自己小的个数,Big为比自己大的个数。1为左边,2为右边。
见图:(左右表示位置关系,高低表示ab之间和cd之间的大小关系)

程序:

#include<cstdio>
#include<cstring>
#include<algorithm>
#include<stack>
#include <iostream>
using namespace std;


const int maxdot=1000005;

int n;

int b[maxdot];

long long big1[maxdot],small1[maxdot];
long long big2[maxdot],small2[maxdot];
long long tree[maxdot];


struct Tnode 
{
    int x,y;
} a[maxdot];

void in(int x)                             //树状数组以数为下标添加标记
{
    for (int i=x;i<=n;i=i+(i-(i&(i-1))))
    {
        tree[i]++;
    }
}

int ask(int x)                            //求和方式求出1-x标记数(小于) 
{
    int re=0;
    for (int i=x;i!=0;i=i&(i-1))
    {
        re+=tree[i];
    }
    return re; 
}


bool cmp(Tnode a,Tnode b)
{
    return a.x<b.x;
}

void solve()
{
    for (int i=1;i<=n;i++) {scanf("%d",&a[i].x);a[i].y=i;}   //离散化 
    sort(a+1,a+n+1,cmp);
    int now=0;
    for (int i=1;i<=n;i++)
    {
        if ((i==1) || (a[i].x!=a[i-1].x)) now++;
        b[a[i].y]=now;
    }
    memset(tree,0,sizeof(tree)); 
    for (int i=1;i<=n;i++)
    {
        small1[i]=ask(b[i]-1);                                //查询 
        big1[i]=(i-1)-ask(b[i]);                              //求大于 
        in(b[i]);                                             //添加标记 
    }
    memset(tree,0,sizeof(tree));
    for (int i=n;i>=1;i--)                                    //反向求c,d 
    {
        small2[i]=ask(b[i]-1);
        big2[i]=(n-i)-ask(b[i]);
        in(b[i]);
    }

    long long ans=0;
    long long ans1=0;
    long long ans2=0;
    //容斥原理 
    for (int i=1;i<=n;i++)
    {
        ans1+=small1[i];                    //合法a,b组 
    }

    for (int i=1;i<=n;i++)                   //合法c,d组 
    {
        ans2+=big1[i];
    }

    ans=ans1*ans2;

    for (int i=1;i<=n;i++)                 //去掉有数相等的情况
    {
        ans-=small2[i]*big2[i];
        ans-=small1[i]*small2[i];
        ans-=big1[i]*big2[i];
        ans-=small1[i]*big1[i];
    }

    cout << ans << endl;
}


int main()
{
    //freopen("world.in","r",stdin);
    //freopen("world.out","w",stdout);
    while(~scanf("%d",&n))
    {
        solve();
    }
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值