• codeforeces近日题目小结


    upd:2019-12-20

    题目源自codeforces的Round_551

    Round_551/F:

    牛逼的dp

    upd:2019-12-14

    题目源自codeforces的Round_552 

    Round_552/F:

    这个题一开始按照背包的经典dp转移去写,外面枚举物品,里面枚举状态,就WA

    仔细思考之后发现这个问题里,把sp offer看作背包问题中的物品

    两个不同物品放入背包的先后顺序,对答案是有影响的

    为了避免这种影响,应该外层枚举状态,内层枚举物品。这样就可了

    Round_552/G:

    智力题。先处理两个数相同 lcm(x, x)  = x的情况。然后再做下面。

    假设ai和aj为最优解,那么有g=gcd(ai, aj)

    枚举g,找到g的倍数中在a里出现过的最小的两个不同数x*g和y*g,用x*y*g更新答案即可

    由于我们枚举g从1-1e7,所以一定能够枚举到gcd(ai, aj),此时最优解一定会更新答案,所以保证正确

    upd:2019-12-13

    题目源自codeforces的Round_602_div1+2, Round_603_div2, Educational_Round_77

    Round_603/F:

    考虑一个经典dp状态定义, dp[i][j]代表上面一个树的最后一个被选取的叶节点编号是i

    下面的树最后一个被选取的叶节点编号是j且 i != j

    那么我们需要考虑第 max(i, j) + 1 个点是选择上下哪棵树的点

    这个代价其实就是比较好求的了

    Round_602_div1/E:

    我直接参考的最短的代码

    一个比较牛逼的构造,正确性自己手画一下是可以证明的

    我先证明了相邻两行必然不等,然后证明的 1 <= i < j <= n 必有第i行和第j行不等

    然后证明1 <= i <= n 必有第i行和第 n+1 行不等

    Round_603_div1/F:

    一个比较牛逼的分治

    Educational_Round_77/F:

    一个比较牛逼的树上计数

    upd:2018-11-25

    题目源自codeforeces的三场contest

    contest/1043+1055+1076

    目前都是solved 6/7,都差了最后一题

    简单题:

    contest/1043/E:

    先不考虑m个限制,求出每个人与其他所有人组队的情况下这个人获得的分数

    对于当前的 i ,如果他与 j 组队时 i 做第一题,则有 xi + yj <= xj + yi

    即 xi - yi <= xj - yj,排序累加计算即可

    contest/1055/C:

    注意到 la 与 lb 的差距会由于 t0 和 t1 而变化 k*gcd(t0, t1), k为系数

    所以肯定想让 la 和 lb 离得尽量近,重合部分也就越大

    能让两个位置重合就重合,不能重合就在那个位置前后蹭蹭就行了

    contest/1076/D:

    考虑最短路的dij算法,发现那些在最短路上的边形成了一棵树

    所以直接跑堆优化dij

    contest/1076/E:

    kdtree模板题,变成二维空间操作,一维dep,一维dfn

    二维空间矩形加+单点查询?考虑差分变为,单点加+前缀和

    查询是在所有操作完成后,所以直接把操作和查询混在一起

    按照第一关键字x,第二关键字y排序,排序后直接树状数组维护即可

    O(nlogn)

    思维僵化,有个O(n)做法,使用差分数组 f[ ]

    把每个操作(v, d, x)挂到节点 v 上

    然后一遍dfs,在到达v的时候对于节点上每个操作

    f[dep[v]] += x, f[dep[v] + d + 1] -= x

    dfs 回到点 v 父亲之前再做逆操作

    对f[ ]求[1, dep[u]]的前缀和即为点 u 的答案

    我傻逼了好久的题目:

    contest/1055/D:

    先对于那些w[i] != v[i]的所有串

    求出他们共同的核心替换部分(必须替换并且长度一致)

    然后为了不让无辜串也被替换所以要尝试将该串尽量向左右拓展

    最后求出来替换串 s -> t 之后再验证,验证一开始想的太简单了

    w[i] = v[i]的串,都满足w[i].find(s) == 0是不足够的

    还会有别的情况!

    简单暴力就是对n个串w[i]都find一下s,第一次找到就替换成 t

    然后新串与v[i]对比即可

      1 #include <bits/stdc++.h>
      2 
      3 #define lb(x) (x&(-x))
      4 
      5 typedef long long ll;
      6 
      7 using namespace std;
      8 
      9 const int N = 5010;
     10 
     11 string s = "", t;
     12 
     13 int n, flag[N];
     14 
     15 string a[N], b[N];
     16 
     17 int nex[N], l[N], r[N];
     18 
     19 vector <int> lt;
     20 
     21 void calc_next() {
     22     nex[0] = -1;
     23     for (int i = 1; i < s.size(); i ++) {
     24         int j = nex[i - 1];
     25         while (j != -1 && s[j + 1] != s[i]) j = nex[j];
     26         if (s[j + 1] == s[i]) nex[i] = j + 1;
     27         else nex[i] = -1;
     28     }
     29 }
     30 
     31 void kmp(string &st) {
     32     for (int i = 0, j = -1; i < st.size(); i ++) {
     33         while (j != -1 && s[j + 1] != st[i]) j = nex[j];
     34         if (s[j + 1] == st[i]) {
     35             j ++;
     36             if (j + 1 == s.size()) {
     37                 st = st.substr(0, i + 1 - s.size()) + t + st.substr(i + 1);
     38                 return;
     39             }
     40         }
     41     }
     42 }
     43 
     44 int main() {
     45     ios::sync_with_stdio(false);    
     46     cin >> n;
     47     for (int i = 1; i <= n; i ++) cin >> a[i];
     48     for (int i = 1; i <= n; i ++) cin >> b[i];
     49     for (int i = 1; i <= n; i ++) {
     50         l[i] = -2, r[i] = -2;
     51         for (int j = 0; j < a[i].size(); j ++) {
     52             if (a[i][j] != b[i][j]) {
     53                 if (l[i] == -2) l[i] = j;
     54                 r[i] = j;
     55             }
     56         }
     57         if (l[i] == -2) continue;
     58         if (s == "") s = a[i].substr(l[i], r[i] - l[i] + 1), t = b[i].substr(l[i], r[i] - l[i] + 1);
     59         else if (s != a[i].substr(l[i], r[i] - l[i] + 1) || t != b[i].substr(l[i], r[i] - l[i] + 1)) {
     60             cout << "NO";
     61             return 0;
     62         }
     63         lt.push_back(i);
     64     }
     65     while (1) {
     66         int flag = 1;
     67         for (int i : lt) {
     68             l[i] --;
     69             if (l[i] < 0) {
     70                 flag = 0;
     71                 break;
     72             }
     73         }
     74         if (!flag) break;
     75         char ch = a[lt[0]][l[lt[0]]];
     76         for (int i : lt) {
     77             if (ch != a[i][l[i]]) {
     78                 flag = 0;
     79                 break;
     80             }
     81         }
     82         if (!flag) break;
     83         s = ch + s;
     84         t = ch + t;
     85     }
     86     while (1) {
     87         int flag = 1;
     88         for (int i : lt) {
     89             r[i] ++;
     90             if (r[i] >= a[i].size()) {
     91                 flag = 0;
     92                 break;
     93             }
     94         }
     95         if (!flag) break;
     96         char ch = a[lt[0]][r[lt[0]]];
     97         for (int i : lt) {
     98             if (ch != a[i][r[i]]) {
     99                 flag = 0;
    100                 break;
    101             }
    102         }
    103         if (!flag) break;
    104         s += ch;
    105         t += ch;
    106     }
    107     calc_next();
    108     for (int i = 1; i <= n; i ++) {
    109         kmp(a[i]);
    110         if (a[i] != b[i]) {
    111             cout << "NO";
    112             return 0;
    113         }
    114     }
    115     cout << "YES
    " << s << '
    ' << t;
    116     return 0;
    117 }
    View Code

    其他题目:

    contest/1055/F:

    树上异或路径和,把每个点的权值变为到根的异或路径和

    异或路径和就变为了两点的权值异或和

    这个题如果问有多少条路径异或和<=x

    就可以直接树分治+trie树,O(nlogn^2)

    但是问排名为k的,套个二分O(nlogn^3),gg

    直接在trie树上搞,从高到低考虑当前位取0的结果个数

    超过了k,则ans当前位取0,否则当前位取1

    空间O(62*n)会爆MLE,直接一层一层的构建trie树

     1 #include <bits/stdc++.h>
     2 
     3 using namespace std;
     4 
     5 const int N = 2e6 + 10;
     6 
     7 int cnt, t, sz[N];
     8 
     9 int n, a[N], b[N], ch[N][2];
    10 
    11 long long s, k, ans, v[N];
    12 
    13 int pos(int x, int y) {
    14     return ch[x][y] ? ch[x][y] : ch[x][y] = ++ cnt;
    15 }
    16 
    17 int main() {
    18     ios::sync_with_stdio(false);
    19     cin >> n >> k;
    20     for (int p, i = 2; i <= n; i ++)
    21         cin >> p >> v[i], v[i] ^= v[p];
    22     for (int i = 1; i <= n; i ++)
    23         a[i] = b[i] = 1;
    24     for (int j = 61; ~j; j --) {
    25         for (int i = 1; i <= cnt; i ++) ch[i][0] = ch[i][1] = sz[i] = 0;
    26         s = t = cnt = 0;
    27         for (int i = 1; i <= n; i ++) sz[a[i] = pos(a[i], v[i] >> j & 1)] ++;
    28         for (int i = 1; i <= n; i ++) s += sz[ch[b[i]][v[i] >> j & 1]];
    29         if (s < k) ans |= 1ll << j, k -= s, t = 1;
    30         for (int i = 1; i <= n; i ++) b[i] = ch[b[i]][(v[i] >> j & 1) ^ t];
    31     }
    32     cout << ans;
    33     return 0;
    34 }
    View Code

    几个DP题目:

    contest/1043/F:

    给出n个数,问最少选几个数可以使得gcd=1

    考虑最优解集合,先拿出第一个数

    然后依次拿出其他数跟它求gcd,那么gcd一定是逐次减小的

    并且每次约去的质因数都是不同的(不然这个数没有意义不会出现在最优集合里)

    ai <= 3e5,可以求出ans如果存在一定 ans <= 7

    设计dp[i][j]代表去除 i 个不同数字使得他们gcd为 j 的方案数有多少种

    i 从小到大枚举, for i 1 -> 7

    dp[i][j] 考虑容斥求出,取 i 个数的gcd为 j 的倍数的方案数

    减去 gcd 为 j*2, j*3, j*4 的方案数即为我们需要的方案数了

    dp[i][1] != 0,则取 i 个数能 gcd = 1

    O(k*nlogn),k为系数,最大是 7

    考虑中间需要组合数,而C(n,  7)会爆long long

    但注意到我们的不需要结果的具体方案数,只想知道dp[i][1] != 0

    所以考虑直接模大质数即可,自然溢出也可以

    再不相信就直接取两个大质数跑两次验证

     1 #include <bits/stdc++.h>
     2 
     3 using namespace std;
     4 
     5 const int N = 3e5 + 10;
     6 
     7 const int Mod = 1e9 + 7;
     8 
     9 int n, m, a[N], b[N];
    10 
    11 int c[N], dp[N], cnt[N];
    12 
    13 int main() {
    14     ios::sync_with_stdio(false);    
    15     cin >> n;
    16     for (int i = 1; i <= n; i ++)
    17         cin >> a[i], m = max(m, a[i]), b[a[i]] ++;
    18     for (int i = 1; i <= m; i ++) {
    19         for (int j = i; j <= m; j += i)
    20             cnt[i] += b[j];
    21         c[i] = 1;
    22     }
    23     for (int k = 1; k < 8; k ++) {
    24         for (int i = m; i; i --) {
    25             dp[i] = (c[i] = 1ll * c[i] * (cnt[i] + 1 - k) % Mod);
    26             for (int j = i << 1; j <= m; j += i)
    27                 dp[i] = (dp[i] - dp[j]) % Mod;
    28         }
    29         if (dp[1] != 0) {
    30             printf("%d
    ", k);
    31             return 0;
    32         }
    33     }
    34     puts("-1");
    35    
    View Code

    contest/1055/E:

    这题面有毒啊,应该用set不是multiset啊

    直接二分答案x进行验证

    是否可以取出m个区间,使得m个区间组成的set里<=x的数字>=k个

    dp[i][j]代表假设总区间只有[1, i]

    选择了 j 个区间后,最多能包含多少个<=x的数字  

    枚举 i, 考虑dp[i][j],只有包含 i 和不包含 i

    不包含 i -> dp[i - 1][j]

    包含 i, 找到所有包含i的区间里最小的左端点L -> dp[L - 1][j - 1] + count(ai <= x for i in [L, i])

     1 #include <bits/stdc++.h>
     2 
     3 using namespace std;
     4 
     5 const int N = 1510;
     6 
     7 int n, m, s, k;
     8 
     9 int a[N], l[N], r[N], dp[N][N];
    10 
    11 bool check(int x) {
    12     memset (dp, 0, sizeof dp);
    13     for (int i = 1; i <= n; i ++) {
    14         int pos = i + 1, sum = 0;
    15         for (int j = 1; j <= s; j ++)
    16             if (l[j] <= i && i <= r[j])
    17                 pos = min(pos, l[j]);
    18         for (int j = pos; j <= i; j ++)
    19             sum += a[j] <= x;
    20         for (int j = 1; j <= m; j ++)
    21             dp[i][j] = max(dp[i - 1][j], dp[pos - 1][j - 1] + sum);
    22     }
    23      return dp[n][m] >= k;
    24 }
    25 
    26 int main() {
    27     ios::sync_with_stdio(false);
    28     int L = 1e9, R = 1, mid, ans = -1;
    29     cin >> n >> s >> m >> k;
    30     for (int i = 1; i <= n; i ++)
    31         cin >> a[i], L = min(L, a[i]), R = max(R, a[i]);
    32     for (int i = 1; i <= s; i ++)
    33         cin >> l[i] >> r[i];
    34     while (L <= R) {
    35         if (check(mid = L + R >> 1)) ans = mid, R = mid - 1;
    36         else L = mid + 1;
    37     }
    38     cout << ans;
    39     return 0;    
    40 }
    View Code

    contest/1076/F:

    dp[i][j]代表第 i 页以 type j 结尾的话,最少结尾是几个连续的type j

    因为考虑当前页以type x结尾的话

    如果下一页的全局最优答案是以type x开始的话,那肯定希望当前页结尾的x越少越好

    如果不是type x开始的话,那么当前页只要以x结尾,多少个都无所谓啦

    所以我们需要这个状态!

    min(dp[n][0], dp[n][1])即为答案

    转移就贪心转移

     1 #include <bits/stdc++.h>
     2 
     3 #define lb(x) (x&(-x))
     4 
     5 using namespace std;
     6 
     7 const int N = 3e5 + 10;
     8 
     9 typedef long long ll;
    10 
    11 ll n, k, a[N][2], dp[N][2];
    12 
    13 int main() {
    14     ios::sync_with_stdio(false);    
    15     cin >> n >> k;
    16     for (ll i = 1; i <= n; i ++) cin >> a[i][0];
    17     for (ll i = 1; i <= n; i ++) cin >> a[i][1];
    18     for (ll s0, min0, s1, min1, i = 1; i <= n; i ++) {
    19         dp[i][0] = dp[i][1] = k + 1;
    20         if (dp[i - 1][0] <= k) {
    21             s0 = dp[i - 1][0] + a[i][0];
    22             min1 = (s0 + k - 1) / k - 1;
    23             if (a[i][1] >= min1 && a[i][1] <= (a[i][0] + 1) * k) {
    24                 if (a[i][1] == min1) dp[i][0] = min(dp[i][0], s0 - k * min1);
    25                 else if (a[i][1] > a[i][0] * k) dp[i][1] = min(dp[i][1], a[i][1] - a[i][0] * k);
    26                 else dp[i][0] = dp[i][1] = 1;
    27             }
    28         }
    29         if (dp[i - 1][1] <= k) {
    30             s1 = dp[i - 1][1] + a[i][1];
    31             min0 = (s1 + k - 1) / k - 1;
    32             if (a[i][0] >= min0 && a[i][0] <= (a[i][1] + 1) * k) {
    33                 if (a[i][0] == min0) dp[i][1] = min(dp[i][1], s1 - k * min0);
    34                 else if (a[i][0] > a[i][1] * k) dp[i][0] = min(dp[i][0], a[i][0] - a[i][1] * k);
    35                 else dp[i][0] = dp[i][1] = 1;
    36             }
    37         }
    38     }
    39     cout << (dp[n][0] <= k || dp[n][1] <= k ? "YES" : "NO");
    40     return 0;
    41 }
    View Code
  • 相关阅读:
    NLP(二十九)一步一步,理解Self-Attention
    树莓派4B踩坑指南
    树莓派4B踩坑指南
    树莓派4B踩坑指南
    【2020.4.17】发现GitHub图片又裂了
    右键管理-干掉多余又删不掉的access
    Python format参数中的列表和元组可以使用“*”
    树莓派4B踩坑指南
    树莓派4B踩坑指南
    树莓派4B踩坑指南
  • 原文地址:https://www.cnblogs.com/ytytzzz/p/10016474.html
Copyright © 2020-2023  润新知