2103年的题目真是超神了!!!
题目大意不想说,直接上题目了。
题目
在幻想乡拨打不同的电话号码收费标准是完全不同的。博丽神社有一个古老的费用表,来确定拨打电话的花费。
每一个幻想乡的电话号码由11 位数字组成。费用表共有N 行,每一行给出一个号码的前缀范围和对应收费标准的名称。对于给出的前缀,如4239-241 ,指所有的前缀为4239,4240,4241 的电话号码。要确定拨打每个号码的收费标准,需要从费用表的第一行开始依次向下查找,第一个匹配的号码前缀范围所对应的收费标准即为拨打这个号码的收费标准。如果没有找到匹配的前缀,那么认为此号码无效。无效号码的收费标准定义为’invalid’(不带引号)。一种收费标准的名称可以在表上多次出现。
博丽神社的费用表已经很旧了,包含了很多很多的数据,这使得管理变得非常困难。神社的巫女决定将这份费用表修改到更简单易读的格式。她希望表内所有号码前缀都按照字典序排序,并且没有‘-’符号,同时没有一个串是另一个串的前缀,且串的总个数最少。’invalid’
和原先不存在的收费标准不能出现在新费用表中。
输入
每组数据的第一行有一个整数N,含义如上所述。
接下来N 行,描述了一张费用表,每行元素按顺序分别为前缀A,字母‘-’,前缀B,该收费标准的名字。前缀最多有11 个数字,收费标准由1~20 个小写字母组成。保证B 的长度<=A 的长度,且A 的后|B|个数字的小于等于B。
特别暴力的想法
建出一棵Trie,暴力插入在 Ai 与 Bi 之间的长度为 11 的数字,最后直接把整棵Trie扫一遍输出就好了。
更加先进的想法
我们发现,其实在 Trie 中有一些子树是没有必要将其中所有的节点都开的,因为在插入 Ai 到 Bi 之间的数字时,很大几率会存在有子树会变满的情况,又由于优先级的问题,我们后面插入到 Trie 中的数字不会影响当前插入的,所以可以给每个 Trie 中的节点一个标记,表示这棵子树的收费标准是否相同,这样就不必插入所有的数字,最后扫一遍求答案就好了。
贴一下代码:
#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cmath>
#include<numeric>
#include<cstring>
#include<queue>
#include<functional>
#include<set>
#include<map>
#define fo(i,a,b) for(int i=a;i<=b;i++)
#define fd(i,a,b) for(int i=a;i>=b;i--)
using namespace std;
const int MAXN = 110;
const int LIM = 100010;
int get(){
char ch;
while (ch=getchar(),(ch<'0'||ch>'9')&& ch!='-');
char c=ch;
int s=0;
if (c!='-')s=c-48;
while (ch=getchar(),ch>='0'&&ch<='9')s=s*10+ch-48;
return c=='-'?-s:s;
}
char s[MAXN][25];
int ls[MAXN];
int l[15],r[15],a[LIM][10];
int len1,len2;
int tot,cost[LIM],ans,n;
bool pd[LIM],vis[LIM];
bool l0(int w){
fo(i,w,len1)
if (l[i])return 0;
return 1;
}
bool r9(int w){
fo(i,w,len1)
if (r[i]<9)return 0;
return 1;
}
void inse(int now,int w,int id){
if (pd[now])return;
if (w>11||(now&&l0(w)&&r9(w)&&!vis[now])){
cost[now]=id;
pd[now]=1;
return;
}
vis[now]=1;
char L[15],R[15];
fo(i,1,11)L[i]=l[i],R[i]=r[i];
int x=l[w],y=r[w];
if (x==y){
if (!a[now][x])a[now][x]=++tot;
inse(a[now][x],w+1,id);
return;
}
fo(i,w+1,11)l[i]=0,r[i]=9;
fo(i,x,y)
if(!a[now][i])a[now][i]=++tot;
fo(i,x+1,y-1){
inse(a[now][i],w+1,id);
}
fo(i,w+1,11)l[i]=L[i];
inse(a[now][x],w+1,id);
fo(i,w+1,11)l[i]=0,r[i]=R[i];
inse(a[now][y],w+1,id);
fo(i,w,11)l[i]=L[i],r[i]=R[i];
}
bool check(int x){
return (ls[x]!=7||s[x][1]!='i'||s[x][2]!='n'||s[x][3]!='v'||s[x][4]!='a'||s[x][5]!='l'||s[x][6]!='i'||s[x][7]!='d');
}
void get_cost(int now,int len){
if (pd[now]&&now&&check(cost[now])){
fo(i,1,len-1)printf("%d",l[i]);
printf(" ");
fo(i,1,ls[cost[now]])printf("%c",s[cost[now]][i]);
printf("\n");
}
if (pd[now])return;
fo(i,0,9)if (a[now][i]){
l[len]=i;
get_cost(a[now][i],len+1);
}
}
void get_ans(int now){
if (pd[now]&&now&&check(cost[now]))ans++;
if (pd[now])return;
fo(i,0,9)if (a[now][i])get_ans(a[now][i]);
}
bool diff(int x,int y){
if (ls[x]!=ls[y])return 1;
fo(i,1,ls[x])
if (s[x][i]!=s[y][i])return 1;
return 0;
}
void getdown(int now){
if (pd[now])return;
int v(0);
fo(i,0,9)if(a[now][i])getdown(a[now][i]);
fo(i,0,9){
if (!a[now][i]||!pd[a[now][i]])return;
if (v&&cost[a[now][i]]&&diff(v,cost[a[now][i]]))return;
else v=cost[a[now][i]];
}
if (!v||!now)return;
cost[now]=v;
pd[now]=1;
}
int main(){
while (scanf("%d",&n)!=EOF){
fo(i,0,tot)cost[i]=pd[i]=0;
fo(i,0,tot)
fo(j,0,9)a[i][j]=0;
fo(i,0,tot)vis[i]=0;
tot=0;
fo(i,1,n){
char ch;
fo(j,1,11)l[j]=0,r[j]=9;
while(ch=getchar(),ch<'0'||ch>'9');
l[len1=1]=ch-'0';
while(ch=getchar(),ch>='0'&&ch<='9')l[++len1]=ch-'0';
while (ch=getchar(),ch<'0'||ch>'9');
r[len2=1]=ch-'0';
while (ch=getchar(),ch>='0'&&ch<='9')r[++len2]=ch-'0';
while(ch=getchar(),ch<'a'||ch>'z');
s[i][ls[i]=1]=ch;
while(ch=getchar(),ch>='a'&&ch<='z')s[i][++ls[i]]=ch;
fd(j,len1,len1-len2+1)r[j]=r[j-len1+len2];
fo(j,1,len1-len2)r[j]=l[j];
inse(0,1,i);
}
getdown(0);
ans=0;
get_ans(0);
printf("%d\n",ans);
get_cost(0,1);
}
}