【GDOI2016模拟3.10】习用之语

题意

给定N个长度均为四的字符串,问他们恰好有K个字母不同的字符串有多少对(相同位置),其中字符集为0~9,a~z. n 50000

分析

十分显然一道题目,暴力 N2 嘛,优化起来很快。
我们考虑一种情况:有L个字符相同——注意并不是恰好——枚举其中不同的位置为 P1...PL 。这样的话求有多少对就很方便了,这是谁都能想到的方法,随便用点算法把原来所有字符串分成若干块(比如排序~~~),每块里的字符串都有指定位置上的字母相同,其他没有指定的位置我们不管。若有X个块,每个块有 Sizex 个字符串,那么这种情况的对数就是 sizex(sizex1)/2
这样我们得到很多种情况的答案。很明显,要恰好,就是容斥一下的问题了,由于K最大只有4,所以明显可以过。

代码

#include<cstdio>
#include<algorithm>
#include<iostream>
#define fo(i,j,k) for(i=j;i<=k;i++)
using namespace std;

const int N=50005;
struct rec
{
    int a[4],cmp;
}b[N];
int a[N][4],n,dd,i,j,c[4],dur,tt,kx,q1,l,rt; 
long long ans,asw[20],as;
char s[4];

bool cmp(rec a,rec b){return a.cmp<b.cmp;}
int cmp1(rec a,rec b)
{
    int i=0;
    while (a.a[i]==b.a[i]&&i<3) i++;
    return i;
}
int get(int x)
{
    int i,j;
    ans=0;
    fo(i,1,n)
    {
        dur=0;
        tt=-1;
        fo(j,0,3)
        if (c[j]!=-1)
        {
            dur=dur*36+a[i][j];
            b[i].a[++tt]=a[i][j];
        }
        fo(j,0,3)
        if (c[j]==-1)
        {
            dur=dur*36+a[i][j];
            b[i].a[++tt]=a[i][j];
        }
        b[i].cmp=dur;
    }
    sort(b+1,b+1+n,cmp);
    dur=1;
    tt=0;
    fo(i,2,n)
    {
        kx=cmp1(b[i],b[i-1]);
        if (kx>=x)
            dur++;
        else
        {
            ans+=(long long)(dur)*(dur-1)/2;
            dur=1;
        }
    }
    ans+=(long long)(dur)*(dur-1)/2;
    return ans;
}
int main()
{
    scanf("%d%d\n",&n,&dd);
    c[0]=c[1]=c[2]=c[3]=-1;
    fo(i,1,n)
    {
        scanf("%s\n",s);
        fo(j,0,3)
        {
            if (s[j]>='0'&&s[j]<='9')
                a[i][j]=s[j]-'0';
            else
                a[i][j]=s[j]-'a'+10;
        }
    }
    int d[17][5]={{0,0,0,0,0},{1,1,1,1,4},{1,1,1,-1,3},{1,1,-1,1,3},{1,-1,1,1,3},{-1,1,1,1,3},{1,1,-1,-1,2},{1,-1,1,-1,2},{1,-1,-1,1,2},{-1,1,1,-1,2},{-1,1,-1,1,2},{-1,-1,1,1,2},{1,-1,-1,-1,1},{-1,1,-1,-1,1},{-1,-1,1,-1,1},{-1,-1,-1,1,1},{-1,-1,-1,-1,0}};
    //这是打情况表
    fo(i,1,16)
    {
        fo(j,0,3) c[j]=d[i][j];
        if (i==13)
        {
            i=i;
        }
        asw[i]=get(d[i][4]);
        for(l=1;l<i;l++)
        {
            rt=0;
            fo(j,0,3)
                if (d[l][j]==d[i][j]&&d[l][j]==1)
                    rt++;
            if (rt==d[i][j]&&d[l][j]>=d[i][j]+1) 
                asw[i]-=asw[l];
        }
        if (4-d[i][4]==dd)
            as+=asw[i];
    }
    printf("%lld\n",as);
}

反思

这道题比赛的时候千辛万苦,改了5遍终于对了,最后对拍错了搞得我交题时还忧心忡忡。问题是很明显的。
1,审题不严谨,条件看着看着忘了。
2,构思的时候十分马虎,先想了算法,就是排序维护一种情况,然而把它打出来了以后我居然还不是很了解这个东西是干什么的,三番五次改程序各种地方,还怀疑是维护的姿势不对。
3,最后没时间了才紧张思考,按逻辑走,很快想通,思想也是十分简单,调试基本不用。
这道题浪费了大量时间做无用功,好的开端是成功的一半啊···

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值