http://codeforces.com/contest/204
A. Little Elephant and Interval
给你l,r,问[l,r]区间内一个数的第一位和最后一位相等的数有多少个。
简单的数位dp,其实随便对数位恶搞一下就能过。
// Author : JayYe Created Time: 2013-11-8 15:43:42 #include <stdio.h> #include <string.h> #include <algorithm> using namespace std; typedef __int64 ll; ll mul[22]; int a[22]; ll solve(ll n) { if(n <= 9) return n+1; int len = 0; while(n) { a[++len] = n%10; n /= 10; } ll ret = 0; for(int i = len-1;i >= 1; i--) { if(i == 1) ret += 10; else ret += mul[i-2]*9; } ret += (a[len]-1)*mul[len-2]; for(int i = len-1;i > 1; i--) { if(a[i] > 0) ret += (a[i])*mul[i-2]; } if(a[len] <= a[1]) ret++; return ret; } ll get(ll n) { ll ret = 0; for(int i = 0;i <= n; i++) { if(i == 0) { ret++; continue; } ll cur = i; int len = 0; while(cur) { a[++len] = cur%10; cur /= 10; } if(a[len] == a[1]) ret++; } return ret; } int main() { mul[0] = 1; for(int i = 1;i <= 17; i++) mul[i] = mul[i-1]*10; ll l, r; scanf("%I64d%I64d", &l ,&r); printf("%I64d\n", solve(r) - solve(l-1)); // printf("%I64d\n", get(r) - get(l-1)); return 0; }
B. Little Elephant and Cards
水题,比A题简单多了。。
C. Little Elephant and Furik and Rubik
给你两串字符串,长度相等都为n,现在从两个字符串中分别取出长度 x (1<=x<=n)的字串,问取出来的串对齐后对应位置字符相同的期望数为多少。 也就是说 取出长度为x的字串a和字串b,ai = bi的期望个数为多少。
思路: 首先考虑某个字符,比如说a字符,这个a字符在串1中很多位置都可能出现,在串2中也一样,假设a字符在串1中i位置有出现,在串2中j位置有出现,那么取出字串后i位置刚好和j位置对应的情况有 min(i, j)* min(n-i+1, n-j+1)种。知道两个位置可以这样子算对应情况后,如果直接枚举两个串相同字符位置的话复杂度是O(n^2)的,可以把所有情况分成三种,第一种是s1[i] = s2[i],第二种是s1[i] = s2[j] (i > j),第三种是 s1[i] = s2(j) (i < j)。对于第一种情况很好算,第二种情况的话,s1[i] = s2[j],因为i > j,所以情况数肯定是 j * (n-i+1)!所以可以直接边预处理边计算情况数,对于字符x前面可以计算出x在s2中位置i的和cnt[x],对于当前s1的字符y,ans += cnt[y]*(n-i+1)即可。复杂度就变成了O(n)了,对于第三种情况也是一样的。
// Author : JayYe Created Time: 2013-11-8 18:32:26 #include <stdio.h> #include <string.h> #include <algorithm> using namespace std; const int maxn = 200000 + 5; char a[maxn], b[maxn]; double pre[33]; int main() { int n; scanf("%d", &n); scanf("%s%s", a, b); double ans = 0; for(int i = 0;i < n; i++) { ans += (double)pre[a[i]-'A']*(n-i); pre[b[i] - 'A'] += i+1; } memset(pre, 0, sizeof(pre)); for(int i = 0;i < n; i++) { ans += (double)pre[b[i]-'A']*(n-i); pre[a[i]-'A'] += i+1; } for(int i = 0;i < n; i++) if(a[i] == b[i]) { ans += (double)(i+1)*(n-i); } double div = 0; for(int i = 1;i <= n; i++) div += (double)i*i; printf("%.10lf\n", ans/div); return 0; }
非常不错的dp。。。
给你一个串,里面有字符B,W,X。X可以是B或者W,问有至少k个连续的B并且右边有至少k个连续的W的情况数有多少种。
思路: 因为可能有多个连续的B或者连续的W存在,所以为了避免计算重复,我就直接找最左边的至少k个连续的B和最右边的至少k个连续的W,两个中间是什么都无所谓。所以要处理出到i位置还没出现过k个连续的B的情况数no_b[i]数组,可以这样子算,首先所有情况很容易计算。如果刚好到i位置出现了k个连续的B,假设小于i的no_b数组计算正确了,因为要使得到i位置刚好出现了k个连续的B,所以 a[ i-k ]必须不能是B,然后no_b[i]要减去no_b[i-k-1]即可。对于计算W的情况也差不多,反向dp一下即可。
计算出到i位置还没出现过k个连续的B的情况数no_b[i]数组后,接下来就很简单了,正向dp然后反向dp下就可以了。
// Author : JayYe Created Time: 2013-11-9 9:36:21 #include <stdio.h> #include <string.h> #include <algorithm> using namespace std; typedef __int64 ll; const int maxn = 1000000 + 5; const int mod = 1000000007; char s[maxn]; ll no_b[maxn], no_w[maxn], tot[maxn], cnt[maxn]; int sb[maxn], sw[maxn]; int exgcd(int a, int b, int &x, int &y) { if(!b) { x = 1; y = 0; return a; } int ret = exgcd(b, a%b, y, x); y -= a/b*x; return ret; } int inv(int a, int mod) { int x, y, d = exgcd(a, mod, x, y); if(x < 0) x += mod; return x; } int main() { int n, k; scanf("%d%d", &n, &k); scanf("%s", s + 1); tot[n+1] = 1; for(int i = n;i >= 1; i--) { if(s[i] == 'X') tot[i] = tot[i+1]*2%mod; else tot[i] = tot[i+1]; } sb[0] = 0; s[0] = 'W'; no_b[0] = 1; cnt[0] = 0; for(int i = 1;i <= n; i++) { if(s[i] == 'X') no_b[i] = no_b[i-1]*2%mod; else no_b[i] = no_b[i-1]; if(s[i] == 'W') sb[i] = 0; else sb[i] = sb[i-1] + 1; cnt[i] = cnt[i-1]; if(sb[i] >= k && (i == k || s[i-k] != 'B')) { ll cur = i == k ? 1 : no_b[i-k-1]; no_b[i] = (no_b[i] - cur)%mod; cnt[i] = (cnt[i] + cur*tot[i+1])%mod; } } no_w[n+1] = no_w[n+2] = 1; sw[n+1] = 0; s[n+1] = 'B'; ll ans = 0; for(int i = n;i >= 1; i--) { if(s[i] == 'X') no_w[i] = no_w[i+1]*2%mod; else no_w[i] = no_w[i+1]; if(s[i] == 'B') sw[i] = 0; else sw[i] = sw[i+1] + 1; if(sw[i] >= k && (n-i+1==k || s[i+k] != 'W')) { ll cur = no_w[i+k+1]; no_w[i] = (no_w[i] - cur)%mod; ans = (ans + cnt[i-1]*cur%mod*inv(tot[i], mod)%mod)%mod; } } printf("%I64d\n", (ans%mod + mod)%mod); return 0; }