Codeforces *1400-1600 做题记录

看了题解的题目会标注*,题目按照 CF 默认顺序排序

C. Skier

题目链接

题面说的是“路径”,而不是“点”。

可以设最初滑雪者的位置在 \(1,1\),用 \(map\) \(vis[id(i,j)][id(k,h)]\)(坐标展开成一维)记录 \((i,j)\) \((k,h)\) 两点之间是否访问过。

 #include <iostream>
 #include <cstring>
 #include <map>
 using namespace std;
 
 long long T, n, tot, x, y, ans;
 string s;
 map <long long, map <long long, long long> > vis;
 map <long long, map <long long, long long> > id;
 
 int main()
 {
     scanf("%lld", &T);
     while(T--)
     {
         cin >> s; n = s.size();
         x = 1, y = 1, ans = 0, tot = 0;
         id[1][1] = ++tot;
         for(int i = 0; i < n; i++)
         {
             int now = id[x][y];
             if(s[i] == 'N') x--;
             if(s[i] == 'S') x++;
             if(s[i] == 'W') y--;
             if(s[i] == 'E') y++;
             if(!id[x][y]) id[x][y] = ++tot;
             if(vis[now][id[x][y]] || vis[id[x][y]][now])
                 ans += 1;
             else ans += 5;
             vis[now][id[x][y]] = vis[id[x][y]][now] = 1;
         }
         x = y = 1;
         for(int i = 0; i < n; i++)
         {
             int now = id[x][y];
             if(s[i] == 'N') x--;
             if(s[i] == 'S') x++;
             if(s[i] == 'W') y--;
             if(s[i] == 'E') y++;
             vis[now][id[x][y]] = vis[id[x][y]][now] = 0;
         }
         id[1][1] = 0, x = y = 1;
         for(int i = 0; i < n; i++)
         {
             if(s[i] == 'N') x--;
             if(s[i] == 'S') x++;
             if(s[i] == 'W') y--;
             if(s[i] == 'E') y++;
             id[x][y] = 0;
         }
         vis[1][1] = 0;
         printf("%lld\n", ans);
     }
     return 0;
 }

C. Phoenix and Distribution

题目链接

首先考虑第一个字母:从小到大选择,如果第一个字母不同,答案就是其中最大的一个;

剩下两种情况:所有的都摞在一起或平均分配,判断这两种情况谁的字典序小

 #include <iostream>
 #include <cstdio>
 #include <cstring>
 #include <algorithm>
 using namespace std;
 
 const int N = 233333;
 int T, n, k, tot, cnt1 = 0, cnt2 = 0;
 int a[N], c[N], d[N];
 string s;
 
 bool same(int x, int y)
 {
     for(int i = x + 1; i <= y; i++)
         if(a[i] != a[i - 1]) return false;
     return true;
 }
 
 bool cmp(int num1, int num2)
 {
     for(int i = 1; i <= min(num1, num2); i++)
     {
         if(c[i] != d[i] && c[i] < d[i]) return true;
         if(c[i] != d[i] && c[i] > d[i]) return false; 
     }
     if(num1 < num2) return true;
     return false;
 }
 
 int main()
 {
     scanf("%d", &T);
     while(T--)
     {
         scanf("%d%d", &n, &k);
         cin >> s;
         for(int i = 0; i < n; i++) a[i + 1] = s[i];
         sort(a + 1, a + n + 1); 
         if(!same(1, k)) 
         {
             cout << (char)(a[k]) << "\n";
             continue;
         }
         tot = 1, cnt1 = cnt2 = 0;
         while(tot <= n)
         {
             c[++cnt1] = a[min(n, tot + k - 1)];
             if(!same(tot, min(n, tot + k - 1))) break;
             tot += k;
         }
         d[++cnt2] = a[1];
         for(int i = k + 1; i <= n; i++) d[++cnt2] = a[i];
         if(cmp(cnt1, cnt2))
         {
             for(int i = 1; i <= cnt1; i++)
                 cout << (char)(c[i]);
             printf("\n");
         }
         else
         {
             for(int i = 1; i <= cnt2; i++)
                 cout << (char)(d[i]);
             printf("\n");
         }
     }
     return 0;
 }

B. Phoenix and Beauty

题目链接

如果看做一个长度为 \(k\) 的窗口从左向右滑动,出去的必须等于进来的。所以,若数组中有超过 \(k\) 个元素,则输出 \(-1\)

否则,构造前 \(k\) 个里面包含所有元素,不断向右滑动,遇到相等的就领走初始数组中的元素,直至初始数组为空

 #include <iostream>
 #include <cstdio>
 #include <cstring>
 #include <algorithm>
 using namespace std;
 
 const int N = 23333;
 int T, n, k, m, tot, a[N], use[N], b[N], c[N], d[N];
 
 int main()
 {
     cin >> T;
     memset(use, 0, sizeof(use));
     while(T--)
     {
         scanf("%d%d", &n, &k);
         int fl = 1, cnt = 0, tmp = 0;
         for(int i = 1; i <= n; i++)
             scanf("%d", &a[i]), c[i] = a[i];
         sort(c + 1, c + n + 1);
         for(int i = 1; i <= n; i++)
             if(c[i] != c[i - 1] || i == 1) d[++tmp] = c[i];
         for(int i = 1; i <= n; i++)
         {
             if(!use[a[i]]) cnt++;
             use[a[i]] = 1;
         }
         if(cnt <= k)
         {
             tot = 1, m = k;
             if(tmp >= k)
                 for(int i = 1; i <= k; i++) b[i] = d[i];
             else
             {
                 for(int i = 1; i <= k; i++) b[i] = 1;
                 for(int i = 1; i <= tmp; i++) b[i] = d[i];
             }
             while(tot <= n)
             {
                 b[++m] = b[m - k];
                 if(b[m] == a[tot]) tot++;
             }
             printf("%d\n", m);
             for(int i = 1; i <= m; i++) printf("%d ", b[i]);
             printf("\n");
         }
         else printf("-1\n");
         for(int i = 1; i <= n; i++) use[a[i]] = 0;
     } 
     return 0;
 }

A. Hilbert's Hotel

题目链接

房间数是无限的,无法枚举,考虑枚举 \(a[1-n]\)。先不考虑取模,最初能 \(+a[i]\) 的就是 \(i\) 号房,顺序推下去,能 \(+a[i]\) 的还有类似 \(i+n\)\(i+2n\)...这些房间 \(mod\) \(n\) 都等于 \(i\),加 \(a[i]\) 之后再取膜也是相等的

\(i\) 号房和 \(j\) 号房转移到了同一个房间,说明 \(i+xn+a[i]=j+a[j]\)\(x\) 是常数),此时 \((a[i]+i)\)%\(n=(j+a[j])\)%\(n\)。因此对于 \(1≤i≤n\),只需要标记\((i+a[i])%n\) 是否使用过

 #include <iostream>
 #include <map> 
 #include <cstdio>
 using namespace std;
 
 const int N = 2333333;
 long long T, n, a[N], vis[N];
 bool tmp;
 
 int main()
 {
     scanf("%lld", &T);
     while(T--)
     {
         scanf("%lld", &n);
         tmp = true;
         for(int i = 0; i < n; i++) scanf("%lld", &a[i]);
         for(int j = 0; j < n; j++)
         {
             vis[(((j + a[j]) % n) + n) % n]++;
             if(vis[(((j + a[j]) % n) + n) % n] > 1)
                 tmp = false;
         }
         for(int j = 0; j < n; j++)
             vis[(((j + a[j]) % n) + n) % n] = 0;
         if(tmp == true) printf("YES\n");
         else printf("NO\n");
     }
     return 0;
 }

C. Yet Another Counting Problem *

题目链接

保证 \(a<b\),打表找规律得从 \(lcm(a,b)*x\)\(x≥0\)\(x\) 是常数)开始 \(b\) 个数 %\(a\)%\(b=\)%\(b\)%\(a\)

注意特判 \(x=0\),判断边界

 #include <iostream>
 #include <cstdio>
 #include <cstring>
 using namespace std;
 
 long long T, a, b, q, l, r, lcm;
 long long get(long long x)
 {
     long long res = (x / lcm + 1) * b;
     if(x % lcm < b) res -= b - 1, res += x % lcm;
     if(x >= b) res - 1;
     return max(0ll, res);
 }
 
 int main()
 {
     scanf("%lld", &T);
     while(T--)
     {
         scanf("%lld%lld%lld", &a, &b, &q);
         if(a > b) swap(a, b);
         for(int i = a * b; i > 0; i--)
             if(i % a == 0 && i % b == 0)
                 lcm = i;
         for(int i = 1; i <= q; i++)
         {
             scanf("%lld%lld", &l, &r);
             printf("%lld ", r - l + 1 - get(r) + get(l - 1));
         }
         printf("\n");
     }
     return 0;
 }

A. Nastya and Strange Generator

题目链接

好像是写麻烦了...

注意到 \(r\) 数组不是变成 \(0\),就是有增无减。所以在改变位置 \(i\) 时,使用并查集维护,把 \(r_i\)\(r_{i+1}\) 合并。然后再用线段树维护 \(count\) 的最值

 #include <iostream>
 #include <cstdio>
 #include <cstring>
 #include <algorithm>
 #define mid ((l + r) / 2)
 using namespace std;
  
 const int N = 233333;
 int T, n, x, max_, fl, a[N], cnt[N], fa[N], t[6666666];
  
 int find(int x)
 {
     return x == fa[x] ? x : fa[x] = find(fa[x]);
 }
  
 void build(int x, int l, int r)
 {
     if(l == r)
     {
         t[x] = 1;
         return ;
     }
     build(x * 2, l, mid);
     build(x * 2 + 1, mid + 1, r);
     t[x] = max(t[x * 2], t[x * 2 + 1]);
 }
  
 void update(int x, int l, int r, int std, int k)
 {
     if(std > r || std < l) return ;
     if(l >= r)
     {
         t[x] += k;
         return ;
     }
     update(x * 2, l, mid, std, k);
     update(x * 2 + 1, mid + 1, r, std, k);
     t[x] = max(t[x * 2], t[x * 2 + 1]); 
 }
  
 int main()
 {
     scanf("%d", &T);
     while(T--)
     {
         scanf("%d", &n);
         build(1, 1, n);
         fl = max_ = 1, fa[n + 1] = n + 1;
         for(int i = 1; i <= n; i++) 
         {
             scanf("%d", &x);
             a[x] = i; fa[i] = i;
             cnt[i] = 1;
         }
         for(int i = 1; i <= n; i++)
         {
             if(cnt[find(a[i])] != t[1])
             {
                 fl = 0;
                 printf("No\n");
                 break;
             }
             int x = find(a[i] + 1), y = find(a[i]);
             if(x != n + 1) update(1, 1, n, x, cnt[y]);
             update(1, 1, n, y, -cnt[y]);
             cnt[x] += cnt[y];
             cnt[y] = 0;
             fa[y] = x;
         }
         if(fl == 1) printf("Yes\n");
     }
     return 0;
 }

A. Powered Addition

题目链接

\(maxx[i]\) 表示 \(i\) 前面(不含)\(a[j]\) 的最大值,\(a[i]\) 最小变为 \(maxx[i]\)。因为它们的差恰好能被一些 \(2\) 的次方数唯一地凑出来,将它改正的时间就是其中的 \(k\),使 \(2^k\)\(≤maxx[i]-a[i]\) 的最大整数

 #include <iostream>
 #include <cstdio>
 #include <cstring>
 using namespace std;
 
 const int N = 233333;
 long long T, n, tot, ans, a[N], base[N], now;
 
 long long check(long long x)
 {
     for(int i = tot; i >= 0; i--)
         if(x >= base[i]) return i + 1;
 }
 
 int main()
 {
     scanf("%lld", &T);
     tot = -1;
     for(long long i = 1; i <= 2000000000; i *= 2)
         base[++tot] = i;
     while(T--)
     {
         scanf("%lld", &n);
         for(int i = 1; i <= n; i++) scanf("%lld", &a[i]);
         ans = 0, now = a[1];
         for(int i = 1; i <= n; i++)
         {
             if(now > a[i])
             {
                 ans = max(ans, check(now - a[i]));
                 a[i] = now;
             }
             now = max(now, a[i]);
         }
         printf("%lld\n", ans);
     }
     return 0;
 }

A. Linova and Kingdom

题目链接

先不考虑工业城市的放法,考虑旅游城市的放法。共需要放 \(n-k\) 个旅游城市,如果它的子树中所有节点都是工业城市,那么在 \(i\) 号放一个旅游城市带来的贡献是 \(siz[i]\)(即以 \(i\) 为根的子树的大小)。如果它有 \(x\) 个祖宗(包括 \(i\) 自己),那么这些祖宗的贡献值都要被 \(-1\)

因为在某个结点放旅游城市时,它的所有祖宗结点肯定已经全部变成了旅游城市,只要按照 \(siz[i]-1-dep[i]\) 排序即可(根节点的深度为 \(0\)

 #include <iostream>
 #include <cstdio>
 #include <cstring>
 #include <algorithm>
 using namespace std;
 
 const int N = 2333333;
 struct edge { int nxt, to; } e[N];
 struct srt { int id; long long val; } st[N];
 int n, k, a, b, cnt = 0, head[N], siz[N], dep[N];
 long long ans = 0;
 
 bool cmp(srt x, srt y) { return x.val > y.val; }
 
 void add(int x, int y)
 {
     e[++cnt] = (edge) { head[x], y };
     head[x] = cnt;
 }
 
 void dfs(int x, int fa)
 {
     siz[x] = 1;
     dep[x] = dep[fa] + 1;
     for(int i = head[x]; i; i = e[i].nxt)
     {
         int v = e[i].to;
         if(v == fa) continue;
         dfs(v, x);
         siz[x] += siz[v];
     }
 }
 
 int main()
 {
     scanf("%d%d", &n, &k);
     dep[0] = -1;
     memset(head, 0, sizeof(head));
     for(int i = 1; i < n; i++)
     {
         scanf("%d%d", &a, &b);
         add(a, b), add(b, a);
     }
     dfs(1, 0);
     for(int i = 1; i <= n; i++)
     {
         st[i].id = i;
         st[i].val = siz[i] - dep[i] - 1;
     }
     sort(st + 1, st + n + 1, cmp);
     for(int i = 1; i <= n - k; i++) ans += st[i].val;
     printf("%lld", ans);
     return 0;
 }

C. Circle of Monsters *

题目链接

嗯看的官方题解 https://codeforces.com/blog/entry/75877

 #include <iostream>
 #include <cstdio>
 #include <cstring>
 #include <algorithm>
 using namespace std;
 
 const long long INF = 1e18;
 long long T, n, num, ans, a[2333333], b[2333333];
 
 int main()
 {
     scanf("%lld", &T);
     while(T--)
     {
         scanf("%lld", &n);
         num = 0, ans = INF;
         for(int i = 1; i <= n; i++)
             scanf("%lld%lld", &a[i], &b[i]);
         for(int i = 1; i <= n; i++)
         {
             int pre = i - 1;
             if(i == 1) pre = n;
             num += max(0ll, a[i] - b[pre]);
         }
         for(int i = 1; i <= n; i++)
         {
             int pre = i - 1;
             if(i == 1) pre = n;
             ans = min(ans, num - max(0ll, a[i] - b[pre]) + a[i]);
         }
         printf("%lld\n", ans);
     }
     return 0;
 }

C. K-Complete Word *

题目链接

要想满足题面要求,字符串不仅本身是回文串,每 \(k\) 位也应该构成回文串。即求所有的 \(i,k*j+i,k*j+k-i+1\) 相等。对于每个 \(i\),在所有这些位置里面选出出现最多的字母,都更改为这个字母。

 #include <iostream>
 #include <cstdio>
 #include <cstring>
 using namespace std;
 
 const int N = 233333;
 int T, n, k, ans, num, max_, a[N], cnt[N];
 string s;
 
 int main()
 {
     scanf("%d", &T);
     memset(cnt, 0, sizeof(cnt));
     while(T--)
     {
         scanf("%d%d", &n, &k);
         cin >> s;
         ans = 0;
         for(int i = 0; i < n; i++) a[i + 1] = s[i];
         for(int i = 1; i <= k; i++)
         {
             num = max_ = 0;
             for(int j = 0; j * k + i <= n; j++)
             {
                 cnt[a[j * k + i]]++;
                 cnt[a[j * k + k - i + 1]]++;
                 num += 2;
                 max_ = max(max_, cnt[a[j * k + i]]);
                 max_ = max(max_, cnt[a[j * k + k - i + 1]]);
             }
             for(int j = 0; j * k + i <= n; j++)
             {
                 cnt[a[j * k + i]] = 0;
                 cnt[a[j * k + k - i + 1]] = 0;
             }
             ans += num - max_;
         }
         printf("%d\n", ans / 2);
     }
     return 0;
 }

B. Composite Coloring *

题目链接

看题解三连...

每个合数 \(x\) 都能整除一个最小的质数,这个质数 \(≤sqrt(x)\)。而 \(sqrt(1000)\) 以下正好有 \(11\) 个质数,所以直接把每个 \(i\) 分到它能整出的最小的质数组。

 #include <iostream>
 #include <cstdio>
 #include <cstring>
 using namespace std;
 
 const int N = 23333;
 int T, n, color, num[N], a[N], col[N];
 int p[] = {0, 2, 3, 5, 7, 11, 13, 17, 19, 23, 29, 31};
 
 int main()
 {
     scanf("%d", &T);
     while(T--)
     {
         scanf("%d", &n);
         color = 0;
         for(int i = 1; i <= 11; i++) num[i] = 0;
         for(int i = 1; i <= n; i++) scanf("%d", &a[i]);
         for(int i = 1; i <= n; i++)
             for(int j = 1; j <= 11; j++)
                 if(a[i] % p[j] == 0)
                 {
                     if(!num[j]) num[j] = ++color;
                     col[i] = num[j];
                     break;
                 }
         printf("%d\n", color);
         for(int i = 1; i <= n; i++) printf("%d ", col[i]);
         printf("\n");
     }
     return 0;
 }

B. Dreamoon Likes Permutations

题目链接

如果一个区间有恰好 \(m\) 个元素,且这些元素的最大值是 \(m\),说明当前区间符合条件

从前往后,从后往前分别记录 \(1-i\) 是否符合条件、\(i-n\) 是否符合条件,枚举断点判断即可

 #include <iostream>
 #include <cstdio>
 #include <cstring>
 using namespace std;
 
 const int N = 2333333;
 int T, n, ans, max_, num, a[N], t1[N], t2[N], p1[N], p2[N];
 
 int main()
 {
     scanf("%d", &T);
     while(T--)
     {
         scanf("%d", &n);
         ans = 0;
         for(int i = 1; i <= n; i++) scanf("%d", &a[i]);
         for(int i = 1; i <= n; i++)
             p1[i] = p2[i] = false, t1[a[i]] = t2[a[i]] = 0;
         max_ = num = 0;
         for(int i = 1; i <= n; i++)
         {
             if(!t1[a[i]]) max_ = max(max_, a[i]), num++;
             else break;
             if(max_ == num) p1[i] = true;
             t1[a[i]]++;
         }
         max_ = num = 0;
         for(int i = n; i > 0; i--)
         {
             if(!t2[a[i]]) max_ = max(max_, a[i]), num++;
             else break;
             if(max_ == num) p2[i] = true;
             t2[a[i]]++;
         }
         for(int i = 1; i < n; i++)
             if(p1[i] && p2[i + 1]) ans++;
         printf("%d\n", ans);
         for(int i = 1; i < n; i++)
             if(p1[i] && p2[i + 1])
                 printf("%d %d\n", i, n - i);
     }
     return 0;
 }

C. Game with Chips

题目链接

有一种奇妙的构造方式,就是先向右移,把所有棋子都移到最后一列;然后都移到第一行。这时 \(k\) 个棋子变成了“一个棋子”。让这些棋子从右上角开始遍历整个棋盘。可以证明这种构造方式的路径总长度不会超过 \(2mn\)(因此不存在 \(-1\) 的情况)

 #include <iostream>
 #include <cstring>
 #include <cstdio>
 using namespace std;
 int T, n, m, k, x, y;
 int main()
 {
     scanf("%d%d%d", &n, &m, &k);
     for(int i = 1; i <= k * 2; i++) scanf("%d%d", &x, &y);
     printf("%d\n", m + n + m * n - 1);
     for(int i = 1; i <= m; i++) printf("R");
     for(int i = 1; i <= n; i++) printf("U");
     for(int i = 1; i <= n; i++)
     {
         if(i % 2 == 0)
             for(int j = 1; j < m; j++) printf("R");
         else for(int j = 1; j < m; j++) printf("L");
         if(i != n) printf("D");
     }
     return 0;
 }

D1. Prefix-Suffix Palindrome (Easy version) *

题目链接

先判断最长有多长的前后缀相等,然后判断中间最长的回文串(左端点与最长前缀相连或右端点与最长的后缀相连)

 #include <iostream>
 #include <cstdio>
 #include <cstring>
 #include <algorithm>
 using namespace std;
 
 const int N = 233333;
 int T, n, l, r, l1, l2, tag, a1, a2, a[N];
 string s, ans;
 
 bool check(int x, int y)
 {
     while(a[x] == a[y] && x <= n && y > 0) x++, y--;
     return x >= y;
 }
 
 int main()
 {
     scanf("%d", &T);
     while(T--)
     {
         cin >> s; n = s.size();
         for(int i = 1; i <= n; i++) a[i] = s[i - 1];
         if(n == 1 || check(1, n)) { cout << s << "\n"; continue; }
         l = 1, r = n;
         while(a[l] == a[r]) l++, r--;
         ans = "", a1 = l, a2 = r;
         for(int i = l; i <= r; i++) if(check(l, i)) a1 = i;
         for(int i = r; i >= l; i--) if(check(i, r)) a2 = i;
         if(a1 - l >= r - a2)
         {
             for(int i = 1; i <= a1; i++) ans += a[i];
             for(int i = r + 1; i <= n; i++) ans += a[i];
         }
         else
         {
             for(int i = 1; i < l; i++) ans += a[i];
             for(int i = a2; i <= n; i++) ans += a[i];
         }
         cout << ans << endl;
     }
     return 0;
 }

C. Ehab and Path-etic MEXs

题目链接

为了不让大权值成为路径上的“最小的未出现”,需要尽量让它出现在路径上。经过某条边的路径数 \(val[i]=siz[i]*(n-siz[i])\)\(siz[i]\) 表示 \(i\) 边向下的子树大小)。将边按照 \(val\) 从大到小排序,\(val\) 大的边放大的权值

 #include <iostream>
 #include <cstdio>
 #include <cstring>
 #include <algorithm>
 #define LL long long
 using namespace std;
 
 const int N = 233333;
 struct edge { LL nxt, to, val, id; } e[N];
 LL n, a, b, cnt = 0, head[N], ans[N], siz[N];
 
 void add(LL x, LL y)
 {
     e[++cnt] = (edge) { head[x], y, 0, cnt };
     head[x] = cnt;
 }
 
 void dfs(LL x, LL fa)
 {
     siz[x] = 1;
     for(int i = head[x]; i; i = e[i].nxt)
     {
         LL v = e[i].to;
         if(v == fa) continue;
         dfs(v, x);
         siz[x] += siz[v];
         e[i].val = siz[v] * (n - siz[v]);
     }
 }
 bool cmp(edge x, edge y) { return x.val > y.val; }
 
 int main()
 {
     scanf("%lld", &n);
     memset(ans, -1, sizeof(ans));
     for(int i = 1; i < n; i++)
     {
         scanf("%lld%lld", &a, &b);    
         add(a, b); add(b, a);
     }
     dfs(1, 0);
     sort(e + 1, e + cnt + 1, cmp);
     for(int i = 1; i < n; i++)
         ans[e[i].id] = n - 2 - i + 1;
     for(int i = 1; i <= cnt; i++)
         if(ans[i] != -1) printf("%lld\n", ans[i]);
     return 0;
 }

D. Pair of Topics

题目链接

为了使 \(i\)\(j\) 都在一边,把这个式子移项:\(a[i]-b[i]=-a[j]+b[j]\)。先按照 \(-a[i]+b[i]\) 排序,对于每个 \(i\) 进行二分查找,统计答案。

 #include <iostream>
 #include <cstdio>
 #include <cstring>
 #include <algorithm> 
 #define LL long long
 using namespace std;
 
 const int N = 233333;
 struct edge { LL a, b; } e[N];
 LL n, ans = 0;
 bool cmp(edge x, edge y) { return x.b - x.a < y.b - y.a; }
 
 LL check(LL x)
 {
     LL l = 0, r = n;
     while(l + 1 < r)
     {
         LL mid = (l + r) >> 1;
         if(e[mid].b - e[mid].a < x) l = mid;
         else r = mid;
     }
     if(e[r].b - e[r].a < x) return r;
     return l;
 }
 
 int main()
 {
     scanf("%lld", &n);
     for(int i = 1; i <= n; i++) scanf("%lld", &e[i].a);
     for(int i = 1; i <= n; i++) scanf("%lld", &e[i].b);
     sort(e + 1, e + n + 1, cmp);
     for(int i = 1; i <= n; i++)
         ans += check(e[i].a - e[i].b);
     for(int i = 1; i <= n; i++)
         if(e[i].a * 2 > e[i].b * 2) ans--;
     printf("%lld", ans / 2);
     return 0;
 }

B. Count Subrectangles

题目链接

稍加观察可以发现,\(a\) 中任何一段连续的 \(1\)\(b\) 中任何一段连续的 \(1\) 都可以组成一个矩阵。那么对于每一段连续的 \(1\),我们都只关心它的长度,而不关心它的位置。可以利用差分统计一下每个长度都出现了几次,选择能被 \(k\) 整除的 \(i\) 统计答案

 #include <iostream>
 #include <cstdio>
 #include <cstring>
 using namespace std;
 
 const int N = 500000;
 long long n, m, k, len, ans = 0, a, b, p1[N], p2[N];
 
 int main()
 {
     scanf("%lld%lld%lld", &n, &m, &k);
     len = 0;
     memset(p1, 0, sizeof(p1));
     memset(p2, 0, sizeof(p2));
     for(int i = 1; i <= n; i++)
     {
         scanf("%lld", &a); 
         if(a == 0) len = 0;
         else len++;
         p1[1]++, p1[len + 1]--;
     }
     len = 0;
     for(int i = 1; i <= m; i++)
     {
         scanf("%lld", &b);
         if(b == 0) len = 0;
         else len++;
         p2[1]++; p2[len + 1]--;
     }
     for(int i = 1; i <= n; i++) p1[i] += p1[i - 1];
     for(int i = 1; i <= m; i++) p2[i] += p2[i - 1];
     for(int i = 1; i <= n; i++)
         if(k % i == 0 && k / i <= m)
             ans += p1[i] * p2[k / i];
     printf("%lld", ans);
     return 0;
 }

C. Remove Adjacent *

题目链接

每次选择一个最大能删除的字母删除,因为它不会影响其他字母的删除。证明如下:

\(1.\) 序列中不存在字母比它大,删它肯定不影响

\(2.\) 序列中存在字母比它大,中间没隔着任何字母。这种情况实际上不存在,因为这种情况下这个字母不是最大可删除的字母

\(3.\) 序列中存在字母比他大,中间隔着一些字母,可以证明中间的字母不可能删干净。

 #include <iostream>
 #include <cstdio>
 #include <cstring>
 using namespace std;
 
 const int N = 2333;
 int n, ans = 0, a[N], del[N];
 string s;
 
 int get(int x, int k)
 {
     int tot = 0;
     if(k < 0)
     {
         for(int i = x - 1; i > 0; i--)
             if(del[i] == false) return a[i];
         return 0x3f3f3f3f;
     } 
     for(int i = x + 1; i <= n; i++)
         if(del[i] == false)    return a[i];
     return 0x3f3f3f3f;
 }
 
 int main()
 {
     scanf("%d", &n);
     cin >> s;
     memset(a, 0x3f, sizeof(a));
     memset(del, false, sizeof(del));
     for(int i = 1; i <= n; i++) a[i] = s[i - 1];
     while(true)
     {
         int max_ = 0, id = -1;
         for(int i = 1; i <= n; i++)
             if(!del[i] && (get(i, -1) == a[i] - 1 || get(i, 1) == a[i] - 1))
                 if(a[i] > max_) max_ = a[i], id = i;
         if(id == -1) break;
         del[id] = true;
     }
     for(int i = 1; i <= n; i++) if(del[i]) ans++;
     printf("%d", ans);
     return 0;
 }

A. Journey Planning

题目链接

将这个式子移项得:\(i-b[i]=j-b[j]\)。设 \(val[i]=i-b[i]\),按照 \(val\) 为排序。对于每一个 \(val\),统计所有 \(val\) 值相等的数的和,在里面取最大值作为答案

 #include <iostream>
 #include <cstdio>
 #include <cstring>
 #include <algorithm>
 using namespace std;
 
 const int N = 2333333;
 struct node { long long id, b, val; } a[N];
 long long n, ans = 0, sum = 0;
 bool cmp(node x, node y)
 {
     return x.b == y.b ? x.id < y.id : x.val < y.val;
 }
 
 int main()
 {
     scanf("%lld", &n);
     for(int i = 1; i <= n; i++)
     {
         scanf("%lld", &a[i].b);
         a[i].id = i;
         a[i].val = a[i].id - a[i].b;
     }
     sort(a + 1, a + n + 1, cmp);
     for(int i = 1; i <= n; i++)
     {
         if(a[i].val != a[i - 1].val || i == 1)
         {
             ans = max(ans, sum);
             sum = a[i].b;
         }
         else sum += a[i].b;
     }
     printf("%lld", max(ans, sum));
     return 0;
 }

B. String Modification *

题目链接

\(*1400\) 还要看题解的垃圾的我...

所以不写辣,甩上题解(逃 https://codeforces.com/blog/entry/74493

 #include <iostream>
 #include <cstdio>
 #include <cstring>
 using namespace std;
 
 const int N = 233333;
 int T, n, id, a[N];
 string s;
 
 int get(int x, int pos)
 {
     if(pos <= n - x + 1) return a[pos + x - 1];
     if((n % 2) == (x % 2)) return a[x - (pos - (n - x + 1))];
     return a[pos - n + x - 1];
 }
 
 int main()
 {
     scanf("%d", &T);
     while(T--)
     {
         scanf("%d", &n);
         cin >> s;
         id = 1;
         for(int i = 1; i <= n; i++) a[i] = s[i - 1];
         for(int i = 2; i <= n; i++)
             for(int j = 1; j <= n; j++)
             {
                 if(get(i, j) < get(id, j))
                 {
                     id = i;
                     break;
                 }
                 if(get(i, j) > get(id, j)) break;
             }
         for(int j = 1; j <= n; j++) cout << (char)(get(id, j));
         printf("\n%d\n", id);
     }
 }

其实后面还做过一些,现在开学了,就先鸽着吧...

posted @ 2020-05-08 14:43  Nyxia  阅读(247)  评论(0编辑  收藏  举报