[关闭]
@01010101 2018-09-09T10:43:18.000000Z 字数 5297 阅读 964

0827


A 多色彩的巧克力
A.cpp /1s /256M
题目描述
奶牛 Bessie 有 N 块巧克力,从左往右排成一行,编号从 0 到 N-1。第 i 块巧克力的颜色是 color[i]。我们定义一个参数MaxLen,它表示:具有相同颜色的连续一段巧克力的最大长度。例如:有 10 块巧克力,颜色分别是: ADDDABBAAB,那么 MaxLen=3,因为有 3 块颜色是 D 的巧克力,而且这 3 块巧克力的位置是连续的。为了使得 MaxLen 最大,Bessie可以交换相邻两块巧克力的位置,但是 Bessie 总共交换的次数不能超过给定的值 swap。那么 MaxLen 的最大值是多少?
输入格式
多组测试数据。
第一行,一个整数 G,表示有 G 组测试数据。1 <= G <=5。
每组测试数据格式如下:
第一行,两个整数 N、swap 。1 <= N <= 50。 1 <= swap <= 2500。
第二行,N 个大写字母,第 i 个字母表示第 i 块巧克力的颜色。
输出格式
共 G 行,每行一个整数。
输入样例
4
7 1
ABCDCBC
7 2
ABCDCBC
9 3
ABBABABBA
9 4
ABBABABBA
输出样例
2
3
4
5
【样例解释】
第二组测试数据:交换后可以变成 ABDCCCB。
第三组测试数据:交换后可以变成 ABBBBAABA。
第四组测试数据:交换后可以变成 AABBBBBAA。

一开始想的是dp,dp[i][j]表示前i个交换j次的最大值。后来发现交换i后面的可能使答案更优,所以放弃了dp,选择了贪心。枚举中心i,考试的时候我还枚举了颜色j,但后来发现,如果中心的颜色和当前色块a[j]的颜色不同时在某些情况下可以得到最优解,但无法得到比中心颜色和当前色块颜色相同的更优解,所以颜色不用枚举,然后用k指针维护当前寻找到的点,l和r表示当前色块的左右端点。

其实考试时巨困,后来去洗了把脸就好了。

附上不优秀的考试代码。

  1. int main(){
  2. T = read();
  3. while(T--){
  4. ans = 0;
  5. n = read();m = read();
  6. scanf("%s",a + 1);
  7. for(int i = 1; i <= n; ++i){//centrol
  8. memset(col,0,sizeof(col));
  9. for(int j = 1; j <= n; ++j){//col
  10. if(col[a[j] - 'A']) continue;
  11. col[a[j] - 'A'] = 1;
  12. int k = 1,l = 0,r = 0,al = m,cnt = 0;
  13. if(a[i] == a[j]) {cnt = 1;l = r = 1;}
  14. for(; i - k >= 1 && i + k <= n; ++k){
  15. if((l == 1 && !r)|| (r == 1 && !l)) l = r = 1;
  16. if(a[j] == a[i - k] && al - (k - l) >= 0){
  17. al -= (k - l);
  18. l++;
  19. cnt++;
  20. }
  21. if(a[j] == a[i + k] && al - (k - r) >= 0){
  22. al -= (k - r);
  23. r++;
  24. cnt++;
  25. }
  26. }
  27. for(;i - k >= 1; ++k)
  28. if(a[j] == a[i - k] && al - (k - l) >= 0){
  29. al -= (k - l);
  30. l++;
  31. cnt++;
  32. }
  33. for(;i + k <= n; ++k)
  34. if(a[j] == a[i + k] && al - (k - r) >= 0){
  35. al -= (k - r);
  36. r++;
  37. cnt++;
  38. }
  39. ans = max(ans,cnt);
  40. }
  41. }
  42. printf("%d\n",ans);
  43. }
  44. return 0;
  45. }

C.Mushroom 的区间
C.cpp/1s/256M
【题目描述】
Mushroom 有一行数,初始时全部是 0。现在 Mushroom 有 m 个区间[L,R],他
希望用以下操作得到新的序列。
从 m 个给定区间中选择一个区间[s,t],把区间中的数对应元素全部翻转。(0 变
1,1 变 0)
请告诉 Mushroom 他能得到多少区间。(模 10^9+7)
【输入格式】
第一行包含两个整数 n,m。表示 n 个数和 m 个区间。
接下来 m 行是所表示的区间。
【输出格式】
一个整数,表示能得到的区间数。
【样例输入】
3 3
1 1
2 2
3 3
【样例输出】
8
【数据范围】
对于 30%的数据,n,m<=20
对于 60%的数据,n,m<=100
对于 100%的数据,n,m<=100000
【样例解释】
每个位置都可以单个修改,所以有 8 种可能。

开始找了几组,画了一下,后来画了Venn图,加上子集的枚举{0},{1},{2},{3},{1,2},{1,3},{2,3},{1,2,3}发现只有一段区间被完全覆盖时,即可以用之前的任意区间组成时,当前区间可以忽略。
所以考虑并查集维护,区间左开右闭。

开始时快速幂没开longlong差点见了祖宗。常数也要强转longlong

  1. ll mul(ll a,ll b){
  2. ll ret = 1;
  3. while(b){
  4. if(b & 1) ret = ret * a % mod;
  5. a = a * a % mod;//!!
  6. b >>= 1;
  7. }
  8. return ret;
  9. }
  10. int Find(int u){
  11. return (fa[u] == u) ? u : fa[u] = Find(fa[u]);
  12. }
  13. int main(){
  14. int a,b;
  15. n = read();m = read();
  16. for(int i = 0; i <= n; ++i) fa[i] = i;
  17. while(m--){
  18. a = read();b = read();
  19. a -= 1;
  20. int x = Find(a);int y = Find(b);
  21. if(x != y){
  22. fa[x] = y;
  23. cnt++;
  24. }
  25. }
  26. printf("%lld\n",mul(2LL,(ll)cnt));
  27. return 0;
  28. }

A
CF 208B
题目大意:
给你n张牌,牌的花色和数字只要有一种一样就能合并。每次只能从最右边开始操作,可以覆盖n-1位置,可以覆盖 n-3位置。
问最后能否将全部牌合并成一堆。

暂时还没有想通。

B
CodeForces 709B
数轴上的越野赛,起点为a,数轴上有n个点,须至少到达n-1个点才算完成比赛。问完成比赛的最小路程。
(1 ≤ n ≤ 100 000,  - 1 000 000 ≤ a ≤ 1 000 000)

水题,排序,要么不到1号点,要么不到n号点。然后在判断先跑左右哪边。

C
CodeForces 361B
题目大意:给出n,输出n的一个排列,使得其中下标i和a[i]的gcd不为1的个数有k个。
(1 ≤ n ≤ 105, 0 ≤ k ≤ n)

原来CF也有水题。
最后k个就令a[i] = i
前面的就奇偶互换。
如:1 2 3 4 5 -> 1 (3 2) (5 4)
1 2 3 4 5 6 -> (2,1) (4,3) (6,5)

D
CF 208C
题目大意:有n个城市,m条双向道路(每条边长度为1),让你添加一个警察局(有一个端点是警察局的边会变成安全边),使得安全边比上所有从1到n的最短路数的最大值。
n (2 ≤ n ≤ 100 )

其实思想不难。n的值极小,怎么应该都能过,结果是没开longlong见祖宗,CF居然报MLE。浪费了1h。
可以先找出从1到n的最短路条数,然后枚举哪个点i成为警察局最优。此处要记录跑最短路到i的入度,和从i跑最短路到n的出度。最后两者相乘*2/1到n的最短路条数.

题解对第22个数据的分析,n = 100 因为假设六个一组形成16个4叉路,剩下四个再形成三叉路,则总最短路条数为2^32*3,大于int范围。

  1. queue <int> q;
  2. void bfs(){
  3. q.push(1);num[1] = 1;vis[1] = 1;
  4. while(!q.empty()){
  5. int u = q.front();q.pop();
  6. for(int i = head[u]; i != -1; i = e[i].nxt){
  7. if(!num[e[i].v]){
  8. dis[e[i].v] = dis[u] + 1;
  9. num[e[i].v] = num[u];
  10. q.push(e[i].v);
  11. a[e[i].v][u] = num[u];
  12. b[u][e[i].v] = 1;
  13. }
  14. else if(dis[e[i].v] == dis[u] + 1){
  15. num[e[i].v] += num[u];
  16. a[e[i].v][u] = num[u];
  17. b[u][e[i].v] = 1;
  18. }
  19. }
  20. }
  21. }
  22. int main(){
  23. int ab,aa;
  24. memset(head,-1,sizeof(head));
  25. scanf("%I64d%I64d",&n,&m);
  26. for(int i = 1; i <= m; ++i){
  27. scanf("%d%d",&aa,&ab);
  28. Adde(aa,ab);Adde(ab,aa);
  29. }
  30. bfs();
  31. double t = num[n],ans = 0;
  32. for(int i = 1; i <= n; ++i){
  33. if(a[n][i]) {vis[i] = 1;q.push(i);cntb[i] += 1;}
  34. }
  35. while(!q.empty()){
  36. int u = q.front();q.pop();
  37. for(int i = 1; i <= n; ++i){
  38. if(a[u][i]){
  39. cntb[i] += cntb[u];
  40. if(!vis[i]){
  41. vis[i] = 1;
  42. q.push(i);
  43. }
  44. }
  45. }
  46. }
  47. for(int i = 1; i <= n; ++i){
  48. if(b[1][i]) {q.push(i);cnta[i] += 1;}
  49. }
  50. while(!q.empty()){
  51. int u = q.front();q.pop();
  52. for(int i = 1; i <= n; ++i){
  53. if(b[u][i] && vis[i]){
  54. if(vis[i] == 1){
  55. vis[i] = 2;
  56. q.push(i);
  57. }
  58. cnta[i] += cnta[u];
  59. }
  60. }
  61. }
  62. ans = max(cnta[n] / t,cntb[1] / t);
  63. for(int i = 2; i < n; ++i) ans = max(ans,(double)2 * (cnta[i] * cntb[i]) / t);
  64. printf("%.10lf\n",ans);
  65. return 0;
  66. }

CodeForces 708A Letters Cyclic Shift
从仅由小写字母组成的字符串s中挑选出一个非空子串,将该子串中的每个字母均替换成前一个字母,如'b'换成'a','c'换成'b',以此类推,特别的,'a'要换成'z',问经过一次转换之后,字典序最小的字符串s为多少

水题,但是没有想到全是a的情况。
从头开始,遇到第一个不是a的字符开始变小,变到第一个a为止。
有一个陷阱是,如果字符串为全a,要把最后一个a变为z。

CF 709D
给出 a00 a01 a10 a11四个数,构造一个01序列。a00表示子序列为“00”数量。其余3个同理。若无合法的序列输出Impossible。

这题在学字符串时就做过,但没有AC,主要是因为此题太坑,特判太多,一定考虑最小值的特例以及最大值在计算过程或结果是否会溢出。
其实思路简单,就是先通过a00,a11计算出0,1的个数,要注意a00为0时,0的个数可能为1.
然后将0放在前面,1放在中间,0又放在最后,前两个构造a01,后两个构造a10,注意微调。

  1. ll check(ll a){
  2. if(!a) return 0;
  3. ll t = sqrt(2 * a);
  4. for(int i = -5; i <= 5; ++i)
  5. if((t + i) > 0 && (t + i) * (t + i - 1) == 2 * a) return t + i;
  6. return -1;
  7. }
  8. int main()
  9. {
  10. scanf("%I64d%I64d%I64d%I64d",&a,&b,&c,&d);
  11. n = check(a);m = check(d);
  12. if(n == -1 || m == -1) {printf("Impossible\n");return 0;}
  13. if(!n && !m){
  14. if(!b && !c) {putchar('0');return 0;}
  15. if(b == 1 && !c) {printf("01");return 0;}
  16. if(!b && c == 1) {printf("10");return 0;}
  17. else {printf("Impossible\n");return 0;}
  18. }
  19. if(n == 0){
  20. if((b + c) && m && (b + c) != m) {printf("Impossible\n");return 0;}
  21. else if(b + c == 0 && m) {for(int i = 1; i <= m; ++i) putchar('1');return 0;}
  22. for(int i = 1; i <= c; ++i) putchar('1');
  23. putchar('0');
  24. for(int i = 1; i <= b; ++i) putchar('1');
  25. }
  26. else if(m == 0){
  27. if((b + c) && n && (b + c) != n) {printf("Impossible\n");return 0;}
  28. else if(b + c == 0 && n) {for(int i = 1; i <= n; ++i) putchar('0');return 0;}
  29. for(int i = 1; i <= b; ++i) putchar('0');
  30. putchar('1');
  31. for(int i = 1; i <= c; ++i) putchar('0');
  32. }
  33. else{
  34. if((b + c) != n * m) {printf("Impossible\n");return 0;}
  35. for(int i = 1; i <= b / m; ++i) putchar('0');
  36. for(int i = 1; i <= m - b % m; ++i) putchar('1');
  37. if(n > b / m) putchar('0');
  38. for(int i = 1; i <= b % m; ++i) putchar('1');
  39. for(int i = 1; i <= n - b / m - 1; ++i) putchar('0');
  40. }
  41. return 0;
  42. }
添加新批注
在作者公开此批注前,只有你和作者可见。
回复批注