personal training 8
本文最后更新于67 天前,其中的信息可能已经过时,如有错误请发送邮件到big_fw@foxmail.com

A. gcd

题目的意思可以转变为是否存在某个b的质因子不在a的质因子集合中?所以我们通过不断将b / gcd(a,b),把 b中与 a 公因子的部分剥离掉,最后如果b是1,说明b的所有质因子都来自 a,否则直接输出b

 void solve() {
     int a,b;
     cin >> a >> b;
     while(__gcd(a,b) != 1) {
         b /= gcd(a,b);
    }
     if(b == 1) {
         cout << -1 << endl;
    }
     else cout << b << endl;
 }

C. Shuffle

给你一个长度为 n 的二进制字符串(即由字符 0 和/或 1 组成的字符串) s 。您最多可以对字符串 s 执行一次以下操作:从 s 中选择一个子串(连续的子序列),其中包含k个字符 1,然后将其洗牌(按您的意愿重新排列子串中的字符)。

计算从 s 最多进行一次此操作可以得到多少个不同的字符串。

题目是对包含k个1的字串进行重排,既然是重排,我们也可以选择只重排其中一部分,这也是一种重排,所以题目可以这样理解——对包含1的数<=k的子串重排,要保证重排后与原来的字符串不同,所以我们可以强制改变首尾的字符,然后对每个子串用组合数计算数目。我感觉这样更好理解,也可以参考另一个大佬的题解

这题发现用 dp 不好处理,于是考虑怎么处理掉本质不同这个限制,就有了一个很妙的办法,我们只关注最终的序列,并不关注在哪个区间操作了,所以可以枚举值发生变化的第一个位置 i 和最后一个位置 j 。发现对于不同的 i,j ,最终的序列是肯定不相同的,因为 i,j 这两个位置的值是一定改变了的。

现在我们处理好了本质不同这个限制,我们还要满足对于 k∈[i,j],ak 中的 1 的个数不能超过 k ,且全局中 1 的个数要大于等于 k ,这使得这一对 i,j 是合法的。i,j 中间的 01 可以随意填,ai,aj 要强制改变,方案数为 (c0*c0+c1) ,c0 , c1 代表可填的 0,1 的个数 原链接

上面还有复杂度为O(n)的做法,但本题O(n2)也可以过。

 int C[N][N],a[N];
 int n,k;
 string s;
 void init() {
     C[0][0] = 1;
     for(int i = 1; i <= n; i++) {
         C[i][0] = 1;
         for(int j = 1; j <= i; j++) {
             C[i][j] = (C[i - 1][j - 1] + C[i - 1][j]) % mod;
        }
    }
 }//组合数数组
 void solve() {
     int ans = 1,sum = 0;
     cin >> n >> k >> s;
     init();
     for(int i = 0; i < sz(s); i++) {
         sum += s[i] - '0';
    }
     if(sum < k) {
         cout << 1 << endl;
         return;
    }//没有等于k的子串
     for(int i = 0; i < sz(s); i++) {
         int cnt = s[i] - '0';
         int n0 = 0,n1 = 0;
         if(s[i] == '0') {
             n0++;
             n1--;
        }
         else {
             n1++;
             n0--;
        }
         for(int j = i + 1; j < sz(s); j++) {
             if(s[j] == '1') {
                 n1++;
                 cnt++;
            }
             else n0++;
             if(cnt > k) break;
             int nn0 = n0,nn1 = n1;
             if(s[j] == '1') nn0--;
             else nn1--;
             if(nn0 >= 0 && nn1 >= 0) {
                 ans = (ans + C[nn0 + nn1][nn0]) % mod;
            }
        }
    }
     cout << ans << endl;
 }//复杂度O(n^2)

E. Book

这题有依赖关系我们可以用拓扑排序,然后用一个数组存每章节在哪一遍会被读懂。有向边表示依赖关系,用拓扑排序 + dp[i] 表示第 i 章在哪一轮能被理解。代码如下

 const int N = 2e5 + 10;
 vector<int> g[N];
 int rd[N];
 
 void solve() {
     int n;
     cin >> n;
     for(int i = 1; i <= n; i++) {
         g[i].clear();
         rd[i] = 0;
    }
 
     for(int i = 1; i <= n; i++) {
         int k; cin >> k;
         for(int j = 0; j < k; j++) {
             int x; cin >> x;
             g[x].push_back(i);
             rd[i]++;
        }
    }
 
     queue<int> q;
     vector<int> dp(n + 1, 1);
     for(int i = 1; i <= n; i++) {
         if(rd[i] == 0) q.push(i);
    }
     int cnt = 0;
     while(!q.empty()) {
         int u = q.front(); q.pop();
         cnt++;
         for(auto v : g[u]) {
             rd[v]--;
             if(rd[v] == 0) q.push(v);
             if (v > u) dp[v] = max(dp[v], dp[u]);
             else dp[v] = max(dp[v], dp[u] + 1);
        }
    }
 
     if(cnt < n) {
         cout << -1 << endl;
    } else {
         int ans = 0;
         for(int i = 1; i <= n; i++) {
             ans = max(ans,dp[i]);
        }
         cout << ans << endl;
    }
 }

F. easy

你的任务是从矩阵中选择一些数字,以便:

  • 从每一行中选出2个数字。
  • 从每列中选出2个数字。

您需要确定所选数字的最小和。

实际上怎么选答案都一样,我们把每一个数字的贡献,拆成行标(x – 1) * n加上列标 y。 那么无论怎么选,行标和列标的贡献之和是确定的,直接输出解即可,记得开long long.

 void solve() {
     int ans = 0;
     int n; cin >> n;
     if(n % 2 == 0) {
         for(int i = 1; i <= n; i += 2) {
             for(int j = i; j < i + 2; j++) {
                 for(int k = i; k < i + 2; k++) {
                     ans += (j - 1) * n + k;
                }
            }
        }
    }
     else {
         for(int i = 1; i <= n; i++) {
             ans += (i - 1) * n + i;
        }
         for(int i = 1; i < n; i++) {
             ans += (i - 1) * n + i + 1;
        }
         ans += (n - 1) * n + 1;
    }
     cout << ans << endl;
 }

G. student

考虑全局最大值,至少得剩一个删不掉,全局最小值,最少得剩一个删不掉;a1,an 一定删不掉。 所以考虑能否删完其他位置。 假设 mx,mn 都不是a1,an ,那么对于一个数字,如果在 mx,mn 中间,则直接删了,如果在他俩左边或者右边,则可以选他们中的一个,搭上a1/an 来 删 所以最后的答案是2+ [全局最大值都在中间] + [全局最小值都在中间] 。还有千万不要忘记特判n = 1的时候😢

 int a[N];
 void solve() {
     int n;
     cin >> n;
     if(n == 1) {
         cout << 1 << endl;
         return;
    }
     for(int i = 1; i <= n; i++) cin >> a[i];
     int mx = *max_element(a + 1, a + 1 + n);
     int mn = *min_element(a + 1, a + 1 + n);
     bool flag1 = true,flag2 = true;
     for (int i = 1; i <= n; i++) {
         if(a[i] == mx && (i == 1 || i == n)) flag1 = false;
         if(a[i] == mn && (i == 1 || i == n)) flag2 = false;
    }
     int ans = 2;
     if (flag1) ans++;
     if (flag2) ans++;
     cout << ans << endl;
 }

I.And

如果有重复元素直接输出0,因为a[i] & x & x & … = a[i] & x,所以我们只考虑最多&两次,否则直接输出-1

 int a[N];
 int vis[N],vis1[N];
 void solve() {
     bool flag = false;
     int n,x; cin >> n >> x;
     for(int i = 1; i <= n; i++) {
         cin >> a[i];
         if(vis[a[i]] == 1) {
             flag = true;
        }
         vis[a[i]] = 1;
    }
     if(flag) {
         cout << 0 << endl;
         return;
    }//如果存在重复元素,输出 0 返回
     for(int i = 1; i <= n; i++ ){
         int t = a[i] & x;
         if(t != a[i] && vis[t] == 1) {
             flag = true;
             break;
        }
    }//如果 t != a[i] 且 t 在 vis 中出现过,输出 1 返回
     if(flag) {
         cout << 1 << endl;
         return;
    }
     for(int i = 1; i <= n; i++) {
         int t = a[i] & x;
         if(vis1[t] == 1) {
             cout << 2 << endl;
             return;
        }
         vis1[t] = 1;
    }//如果 t 在 vis1 中出现过,输出 2 返回
     if(!flag) {
         cout << -1 << endl;
    }

J. Swords

每种类型的剑被拿走的数量是x – a[i],所以可以构成一个数组{ x – a[1], x – a[2], …. ,x – a[n] };因为每个人拿走的剑的数目是相同的,所以这个数组的里的每个数都是z的倍数,它们的最大公因数就是 z,人数 y 就是 他们的和 / z,接下来我们考虑x的取值,因为x – a[i] 一定是大于等于0的,所以x >= a[i]max,要使 y 最小就要让个公因数尽可能的大,所以当我们取x为a[i]max时x – a[i]max = 0,此时这个数列的和也是最小的,若取其他更大数,最大公因数只会更小,甚至为1,并且和也会变大,所以x取a[i]max一定是最优解。代码很简单

 int a[N];
 void solve() {
     int n,mx = 0;
     cin >> n;
     for(int i = 1; i <= n; i++) {
         cin >> a[i];
         mx = max(mx,a[i]);
    }
     int z = 0,sum = 0;
     for(int i = 1; i <= n; i++) {
         int d = mx - a[i];
         z = __gcd(d,z);
         sum += d;
    }
     cout << sum / z << ' ' << z << endl;
 }

L. Win

题目就是让你插入k个字符使得lose的字串数最多,没什么难点,别忘了插完了以后,多余的可以直接加上lose

 int a[5];
 string t,s;
 int k;
 void solve() {
     cin >> k >> s;
     bool flag = false;
     for(int i = 0; i < sz(s); i++) {
         if(flag) {
             bool f = false;
             char last = t.back();
             if(last == 'l') {
                 if(s[i] == 'o' || s[i] == 's' || s[i] == 'e') {
                     t += s[i];
                     f = true;
                }
            }
             else if(last == 'o') {
                 if(s[i] == 's' || s[i] == 'e') {
                     t += s[i];
                     f = true;
                }
            }
             else if(last == 's') {
                 if(s[i] == 'e') {
                     t += s[i];
                     f = true;
                }
            }
             if(!f) {
                 a[sz(t)]++;
                 t = "";
                 flag = false;
            }
        }
         if((s[i] =='l' || s[i] == 'o' || s[i] == 's' || s[i] =='e') && flag ==false){
             t += s[i];
             flag = true;
        }
    }
     if(sz(t) >= 1) {
         a[sz(t)]++;
    }
     int ans = 0;
     for(int i = 4; i >= 1; i--) {
         if(k >= a[i] * (4 - i)) {
             ans += a[i];
             k -= a[i] * (4 - i);
        }
         else {
             ans += min(a[i],k / (4 - i));
             k = 0;
             break;
        }
    }
     if(k > 0) ans += k / 4;
     cout << ans << endl;
 }

暂无评论

发送评论 编辑评论


				
|´・ω・)ノ
ヾ(≧∇≦*)ゝ
(☆ω☆)
(╯‵□′)╯︵┴─┴
 ̄﹃ ̄
(/ω\)
∠( ᐛ 」∠)_
(๑•̀ㅁ•́ฅ)
→_→
୧(๑•̀⌄•́๑)૭
٩(ˊᗜˋ*)و
(ノ°ο°)ノ
(´இ皿இ`)
⌇●﹏●⌇
(ฅ´ω`ฅ)
(╯°A°)╯︵○○○
φ( ̄∇ ̄o)
ヾ(´・ ・`。)ノ"
( ง ᵒ̌皿ᵒ̌)ง⁼³₌₃
(ó﹏ò。)
Σ(っ °Д °;)っ
( ,,´・ω・)ノ"(´っω・`。)
╮(╯▽╰)╭
o(*////▽////*)q
>﹏<
( ๑´•ω•) "(ㆆᴗㆆ)
😂
😀
😅
😊
🙂
🙃
😌
😍
😘
😜
😝
😏
😒
🙄
😳
😡
😔
😫
😱
😭
💩
👻
🙌
🖕
👍
👫
👬
👭
🌚
🌝
🙈
💊
😶
🙏
🍦
🍉
😣
Source: github.com/k4yt3x/flowerhd
颜文字
Emoji
小恐龙
花!
上一篇
下一篇