题目1 : 出勤记录I(水题)
描述
小Hi的算法课老师每次上课都会统计小Hi的出勤记录。迟到会被记录一个L,缺席会被记录一个A,按时上课会被记录一个O。
一学期结束,小Hi的出勤记录可以看成是一个只包含LAO的字符串,例如"OOOOLOOOLALLO……"。
如果小Hi整学期缺席不超过1次,并且没有连续3次迟到,小Hi的出勤记录就算合格。
现在给出小Hi的出勤记录,你能判断他是否合格么?
输入
输入第一行包含一个整数T(1 <= T <= 10),代表测试数据的组数。
以下T行每行一个程度不超过100的字符串S,代表小Hi的出勤记录。
输出
对于每一份出勤记录,输出YES或者NO表示该份记录是否合格。
- 样例输入
-
3 LLOLLALL OLLLOOOO OOAAOOOO
- 样例输出
-
YES NO NO
判断是否有“LLL”出现或者"A"出现的个数超过两次 输出NO,其他为YES题目2 : 出勤记录II(dp)
时间限制:10000ms单点时限:1000ms内存限制:256MB描述
小Hi的算法课老师每次上课都会统计小Hi的出勤记录。迟到会被记录一个L,缺席会被记录一个A,按时上课会被记录一个O。
一学期结束,小Hi的出勤记录可以看成是一个只包含LAO的字符串,例如"OOOOLOOOLALLO……"。
如果小Hi整学期缺席不超过1次,并且没有连续3次迟到,小Hi的出勤记录就算合格。
现在给出字符串的长度N,小Hi想知道长度为N的出勤记录中,合格的记录总共有多少种。
例如长度为3的合格出勤记录有19种:OOO OOL OOA OLO OAO LOO AOO OLL OLA OAL LOL LOA AOL LLO LAO ALO LLA LAL ALL。
输入
一个整数N(1 <= N <= 100000)。
输出
长度为N的合格记录总数。由于结果可能很大,你只需输出结果模109+7的余数。
- 样例输入
-
3
样例输出-
19
可以用dp[i][A][L]表示长度为N序列的串,“A”出现的个数为A, 末尾连续“L”的个数为L个
if(s[i+1] == 'L' && L <= 2 && L >= 1) dp[i+1][A][L] += dp[i][A][L-1]
if( s[i+1] == 'A') dp[i+1][1][0] += dp[i][0][L]
if( s[i+1] == 'O') dp[i+1][A][0] += dp[i][A][L]#include <iostream> #include <cstdio> #include <string> #include <map> using namespace std; const int MAXN = 2e5+10; int A[MAXN], cnt[MAXN]; int n; typedef long long int LL; LL k; map<int, int>Map; LL slove(LL ans){ LL ret = 0, sum = 0; for(int i = 0, j = 0; i < n; i++){ while(j < n && sum+cnt[A[j]] <= ans) { sum += cnt[A[j]]; cnt[A[j]]++; j++; } ret += j-i; cnt[A[i]]--; sum -= cnt[A[i]]; } return ret; } int main() { int T; scanf("%d", &T); while(T--){ scanf("%d%d", &n, &k); for(int i = 0; i < n; i++){ scanf("%d", &A[i]); Map[A[i]] = i; } for(int i = 0; i < n; i++) A[i] = Map[A[i]]; LL L = -1, R = 1LL*n*(n+1); while(L + 1 < R){ LL mid = (L+R)>>1; if(slove(mid) < k) L = mid; else R = mid; } printf("%lld ", R); } return 0; }
题目3 : 区间价值(二分+双指针)
时间限制:10000ms单点时限:1000ms内存限制:256MB描述
给定n个数A1...An,小Ho想了解AL..AR中有多少对元素值相同。小Ho把这个数目定义为区间[L,R]的价值,用v[L,R]表示。
例如1 1 1 2 2这五个数所组成的区间的价值为4。
现在小Ho想知道在所有的的v[L,R](1 <= L <= R <= n)中,第k小的值是多少。
输入
第一行一个数T(T<=10),表示数据组数。
对于每一组数据:
第一行两个数n,k(1<=n<=200,000,1<=k<=n*(n+1)/2)
第二行n个数A1…An(1<=Ai<=1,000,000,000)
输出
一个数表示答案。
- 样例输入
-
2 4 7 1 1 2 3 3 6 100 100 100
- 样例输出
-
0 3
二分答案,用双指针求出[1~n]区间 <= ans的个数
#include <iostream> #include <cstdio> #include <string> #include <map> using namespace std; const int MAXN = 2e5+10; int A[MAXN], cnt[MAXN]; int n; typedef long long int LL; LL k; map<int, int>Map; LL slove(LL ans){ LL ret = 0, sum = 0; //ret 小于等于ans区间的个数 for(int i = 0, j = 0; i < n; i++){ while(j < n && sum+cnt[A[j]] <= ans) { sum += cnt[A[j]]; cnt[A[j]]++; j++;//指针j向右移动 } ret += j-i;//求出满足条件区间的 个数 cnt[A[i]]--;//指针i向右移动 sum -= cnt[A[i]]; } return ret; } int main() { int T; scanf("%d", &T); while(T--){ scanf("%d%d", &n, &k); for(int i = 0; i < n; i++){ scanf("%d", &A[i]); Map[A[i]] = i; } for(int i = 0; i < n; i++) A[i] = Map[A[i]];//使A[i]处在[0~n-1] LL L = -1, R = 1LL*n*(n+1); while(L + 1 < R){ LL mid = (L+R)>>1; if(slove(mid) < k) L = mid; else R = mid; } printf("%lld ", R); } return 0; }