GDOI2016模拟8.14电话表

题目
在幻想乡拨打不同的电话号码收费标准是完全不同的。博丽神社有一个古老的费用表,来确定拨打电话的花费。

每一个幻想乡的电话号码由11 位数字组成。费用表共有N 行,每一行给出一个号码的前缀范围和对应收费标准的名称。对于给出的前缀,如4239-241 ,指所有的前缀为4239,4240,4241 的电话号码。要确定拨打每个号码的收费标准,需要从费用表的第一行开始

依次向下查找,第一个匹配的号码前缀范围所对应的收费标准即为拨打这个号码的收费标准。如果没有找到匹配的前缀,那么认为此号码无效。无效号码的收费标准定义为’invalid’(不带引号)。一种收费标准的名称可以在表上多次出现。

博丽神社的费用表已经很旧了,包含了很多很多的数据,这使得管理变得非常困难。神社的巫女决定将这份费用表修改到更简单易读的格式。她希望表内所有号码前缀都按照字典序排序,并且没有‘-’符号,同时没有一个串是另一个串的前缀,且串的总个数最少。’invalid’

和原先不存在的收费标准不能出现在新费用表中。

这题我们可以用trie树来记录哪些前缀已被使用,这时候,我们可以用区间查询来看一下哪些位置没被找过,找到最近的地方打上标记,并记录下来(除了incalid的),然后排个序,相邻且前缀相邻的属于同一个名字的合并,剩下的就是不同名字的了。

然后将相邻的暴力判断,至少要保留到前几位才能使他们有区别,并记录下来,作为保留的参考,输出时,能压缩的尽量压缩,细节比较多,参考代码

贴代码

#include<iostream>
#include<algorithm>
#include<cstdio>
#define N 3500000
#define M 101
#include<cmath>
#include<cstring>
using namespace std;
int f[N][10];
short int bz[N];
int n,maxnlen,sum;
int len[N],b[N],bz1;
long long help[12],a[M][2],ans[N][2],ll,rr;
int ans1[N];
char name[M][M];
void init(){
    static char c[M];
    static char x;
    static int y;
    for (int i=1;i<=n;i++){
        scanf(" %s",&c);
        len[i]=strlen(c);
        maxnlen=max(maxnlen,len[i]);
        a[i][0]=a[i][1]=0;
        for (int j=0;j<len[i];j++)
            (a[i][0]*=10)+=c[j]-'0';
        scanf(" %c",&x);
        scanf(" %s",&c);
        y=strlen(c);
        for (int j=0;j<y;j++)
            (a[i][1]*=10)+=c[j]-'0';
        a[i][1]+=a[i][0]/help[y]*help[y];
        scanf(" %s",&name[i]);
    }
}
bool cmp(int x,int y){
    return a[x][0]<a[y][0]||(a[x][0]==a[y][0]&&a[x][1]>a[y][1]);
}
void pre(){
    static int x;
    for (int i=1;i<=n;i++){
        x=maxnlen-len[i];
        a[i][0]*=help[x],a[i][1]*=help[x];
        a[i][1]+=help[x]-1;
    }
}
void clear(int x){
    for (int i=0;i<10;i++)
        f[x][i]=0;
    bz[x]=0;
}
bool jiao(long long l,long long r,long long x){
    return l<=x&&x<=r;
}
void change(long long l,long long r,int s,int z){
    static long long ss,sss;
    if (bz[s]==2)return;
    if (ll<=l&&r<=rr&&!bz[s]){
        bz[s]=2;
        if (!bz1)return;
        if (ans1[ans1[0]]==bz1&&ans[ans1[0]][1]==l-1)
            ans[ans1[0]][1]=r;
        else{
            ans1[++ans1[0]]=bz1;
            ans[ans1[0]][0]=l,ans[ans1[0]][1]=r;
        }
        return;
    }
    bz[s]=1;
    for (int i=0;i<=9;i++){
        sss=l+help[z]*(i+1)-1;
        ss=l+help[z]*i;
        if (jiao(ll,rr,ss)||jiao(ll,rr,sss)||(ss<=ll&&rr<=sss)){
            if (!f[s][i])
                f[s][i]=++sum,clear(sum);
            change(ss,sss,f[s][i],z-1);
        }
    }
}
bool cmp1(int x,int y){
    return ans[x][0]<ans[y][0];
}
void write(long long x,int y,int bz){
    for (int i=y-1;i>=0;i--)
        printf("%d",x/help[i]%10);
    printf(" %s\n",name[bz]);
}
bool jian(int x){
    if (strlen(name[x])!=7)return 0;
    if (name[x][0]!='i'||name[x][1]!='n'||name[x][2]!='v'||name[x][3]!='a'||name[x][4]!='l'||name[x][5]!='i'||name[x][6]!='d')
        return 0;
    return 1;
}
bool differ(int x,int y){
    static int len;
    if ((len=strlen(name[x]))!=strlen(name[y]))return 1;
    for (int i=0;i<len;i++)
        if (name[x][i]!=name[y][i])return 1;
    return 0;
}
void work(){
    static int x,y;
    static long long xx,yy,z;
    bz1=0;
    for (int i=1;i<=n;i++){
        if (!jian(i))bz1=i;
        else bz1=0;
        ll=a[i][0],rr=a[i][1],change(0,help[maxnlen]-1,1,maxnlen-1);
    }
    for (int i=1;i<=ans1[0];i++)
        b[i]=i,len[i]=maxnlen-1;
    sort(b+1,b+ans1[0]+1,cmp1);
    if (ans1[0])
    b[0]=1;
    else
        b[0]=0;
    for (int i=2;i<=ans1[0];i++)
        if (differ(ans1[b[b[0]]],ans1[b[i]])||ans[b[b[0]]][1]+1!=ans[b[i]][0])
            b[++b[0]]=b[i];
        else
            ans[b[b[0]]][1]=ans[b[i]][1];
    x=b[1];
    while (len[1]&&(ans[x][0]/help[len[1]-1]%10>0||ans[x][0]%help[len[1]-1]>0))len[1]--;
    for (int i=2;i<=b[0];i++){
        y=b[i],x=b[i-1];
        while (len[i-1]&&ans[x][1]%help[len[i-1]])len[i-1]--;
        while (len[i-1]&&ans[y][0]/help[len[i-1]]==ans[x][1]/help[len[i-1]])len[i-1]--;
        len[i]=len[i-1];
    }
    x=0;
    for (int i=1;i<=b[0];i++){
        xx=ans[b[i]][0]/help[len[i]],yy=ans[b[i]][1]/help[len[i]];
        z=1,y=len[i];
        for (;xx*z<=yy;){
            if (!(xx%10)){
                if ((xx/10+1)*z*10-1<=yy&&y+1<maxnlen)
                    xx/=10,z*=10,y++;
                else
                    if ((xx+1)*z-1>yy)xx*=10,z/=10,y--;
                    else
                        x++,xx++;
            }else
                if ((xx+1)*z-1>yy)xx*=10,z/=10,y--;
                else
                    x++,xx++;
        }
    }
    printf("%d\n",x);
    for (int i=1;i<=b[0];i++){
        xx=ans[b[i]][0]/help[len[i]],yy=ans[b[i]][1]/help[len[i]];
        z=1;
        for (;xx*z<=yy;){
            if (!(xx%10)){
                if ((xx/10+1)*z*10-1<=yy&&len[i]+1<maxnlen)
                    xx/=10,z*=10,len[i]++;
                else
                    if ((xx+1)*z-1>yy)xx*=10,z/=10,len[i]--;
                    else
                        write(xx,maxnlen-len[i],ans1[b[i]]),xx++;
            }else
                if ((xx+1)*z-1>yy)xx*=10,z/=10,len[i]--;
                else
                    write(xx,maxnlen-len[i],ans1[b[i]]),xx++;
        }
    }
}
int main(){
    help[0]=1;
    for (int i=1;i<=11;i++)
        help[i]=help[i-1]*10;
    while (scanf("%d",&n)!=-1){
        maxnlen=0;
        init();
        pre();
        sum=1;
        ans1[0]=0;
        clear(1);
        work();
    }
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值