NOIP2017模拟赛 senior 6.29 T2 小T的钢琴(piano)
Description
Input
Output
这道题思路还是比较好想的,因为题目中已经提到公共子序列长度,所以咯,最长公共子序列好不好!
But,这里我们如果只使用LCS的话那么,这里我们的时间复杂度是O(n^2)。爆了对不对?
怎么优化呢?
对于我们的比较,在这里是比较的字符串对吧,即每一坨音符。使用string可直接比较,所以常数会小一点。
更好一点的优化就是使用哈希表,我们将乐谱Little_T中的字符串哈希,转成数值,然后通过哈希值判断,将乐谱B中的无效的音符去除。这里直接比较哈希值,常数更小。
但是,我们还是不能拿满分对吧。
我们再来优化!
题目中有提到:因为小T是一个放荡不羁的作曲家,所以保证他的乐谱中每个音符都不会重复出现。
看到这里我们是不是还可以优化呢?
有了这个条件,我们可以把LCS问题传唤成LIS(最长上升子序列)问题。
预处理还是一样的,哈希,不过我这里较为简便的直接将每一个字符转换为数值后,如果继续有字符需要加入,那么我们将先前数值乘10再相加。
我们使用二分查找,将乐谱B中数值拿入Little_T中去找,找不到,跳过。
找到了,我们从这里开始使用LIS求解就好了。
1 #include<iostream> 2 #include<cstdio> 3 #include<cstring> 4 #include<algorithm> 5 #include<cmath> 6 #include<string> 7 #define INT_MAX_ 0x7fffffff 8 #define LONG_MAX_ 0x7fffffffffffffffll 9 #define pi acos(-1) 10 #define N 100010 11 #define M 200010 12 using namespace std; 13 14 inline int read() 15 { 16 int data=0,w=1; 17 char ch=0; 18 while(ch!='-' && (ch<'0' || ch>'9')) ch=getchar(); 19 if(ch=='-') w=-1,ch=getchar(); 20 while(ch>='0' && ch<='9') data=data*10+ch-'0',ch=getchar(); 21 return data*w; 22 } 23 24 inline void write(int x) 25 { 26 if(x<0) putchar('-'),x=-x; 27 if(x>9) write(x/10); 28 putchar(x%10+'0'); 29 } 30 31 struct Data 32 { 33 int value,number; 34 } data[N]; 35 inline bool my_comp(const Data &a, const Data &b) 36 { 37 return a.value < b.value; 38 } 39 40 int b[M],ans[N]; 41 char Little[25]; 42 char str[25]; 43 int n,r; 44 45 int main() 46 { 47 freopen("piano.in","r",stdin); 48 freopen("piano.out","w",stdout); 49 50 n = read(); 51 r = read(); 52 for(int i = 1; i <= n; i++) 53 { 54 scanf("%s",Little); 55 int lenth = strlen(Little); 56 data[i].number = i; 57 data[data[i].number].value = (Little[0] - 'A' + 1) * 10 + Little[1] - '0'; 58 if(lenth > 2 && Little[2] == '.') 59 { 60 for(int j = 3; j < lenth; j++) 61 { 62 if(Little[j] == '(' || Little[j] == '[' || Little[j] == '{') break; 63 data[i].value = data[i].value * 10 + Little[j] - '0'; 64 } 65 } 66 } 67 sort(data+1,data+1+n,my_comp); 68 while(r--) 69 { 70 int li = read(); 71 for(int i = 1; i <= li; i++) 72 { 73 scanf("%s",str); 74 int lenth = strlen(str); 75 b[i] = (str[0] - 'A' + 1) * 10 + str[1] - '0'; 76 if(lenth > 2 && str[2] == '.') 77 { 78 for(int j = 3; j < lenth; j++) 79 { 80 if(str[j] == '(' || str[j] == '[' || str[j] == '{') break; 81 b[i] = b[i] * 10 + str[j] - '0'; 82 } 83 } 84 } 85 ans[0] = 0; 86 for(int i = 1; i <= li; i++) 87 { 88 int left = 1, right = n; 89 while(left < right) 90 { 91 int mid = (left + right) / 2; 92 if(data[mid].value >= b[i]) 93 { 94 right = mid; 95 } 96 else 97 { 98 left = mid + 1; 99 } 100 } 101 if(data[left].value != b[i]) continue; 102 if(ans[ans[0]] < data[left].number) ans[++ans[0]] = data[left].number; 103 else 104 { 105 int aider = lower_bound(ans+1,ans+1+ans[0],data[left].number) - ans; 106 ans[aider] = data[left].number; 107 } 108 } 109 printf("%.6lf ",ans[0] * 1.0 / li); 110 } 111 112 return 0; 113 }