codeforeces近日题目小结

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\n" << s << ‘\n‘ << t;
116     return 0;
117 }

其他题目:

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 }

几个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\n", k);
31             return 0;
32         }
33     }
34     puts("-1");
35    

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 }

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 }

原文地址:https://www.cnblogs.com/ytytzzz/p/10016474.html

时间: 2024-10-30 07:08:31

codeforeces近日题目小结的相关文章

ACM -二分图题目小结(更新中)

暂时只包括与最大匹配相关的问题. 求最大独立集,最小路径覆盖等等大多数题目都可以转化为求最大匹配用匈牙利算法解决. 1.最大匹配(边集) 此类问题最直接,直接用匈牙利算法即可. HDU 2063  过山车 http://acm.hdu.edu.cn/showproblem.php?pid=2063 二分图最大匹配模版题. ZOJ 1654 - Place the Robots http://acm.zju.edu.cn/onlinejudge/showProblem.do?problemCode

博弈论类题目小结——转载

出处http://blog.csdn.net/ACM_cxlove?viewmode=contents    by---cxlove 首先当然要献上一些非常好的学习资料: 基础博弈的小结:http://blog.csdn.net/acm_cxlove/article/details/7854530 经典翻硬币游戏小结:http://blog.csdn.net/acm_cxlove/article/details/7854534 经典的删边游戏小结:http://blog.csdn.net/acm

状态压缩题目小结

1.POJ - 3254 Corn Fields 题目大意:有一个n*m的草地(草地上有的是沼泽),现在要分配牛去上面吃草,要求每头牛不能相邻(不能有公共边),问有多少种分配方案,一头牛都不分配也算一种分配方案 解题思路:这是碰到的第一道比较另类的压缩 1.首先考虑一下,每一行该怎么分配牛才不会让他们相邻,可以用状态压缩,0表示不放牛,1表示放牛,枚举一下有多少种可行的方案并纪录下来 2.接着考虑一下,因为不能相邻,而相邻的行之间又会相互影响. 考虑到第一行是比较特殊的,可以先枚举第一行的解决方

CDQ分治题目小结

CDQ分治属于比较特殊的一类分治,许多问题转化为这类分治的时候,时空方面都会有很大节省,而且写起来没有这么麻烦. 这类分治的特殊性在于分治的左右两部分的合并,作用两部分在合并的时候作用是不同的,比如,通过左半部分的影响来更新右半部分,所以分治开始前都要按照某一个关键字排序,然后利用这个顺序,考虑一个区间[l, r]的两部分间的影响.感觉说的太多,还是不如具体题目分析,而且题目也不尽相同,记住几句话是没什么用的. 练习地址: http://vjudge.net/contest/view.actio

博弈题目小结

HDU 2174 kiki's game 题意:有一个N*M的棋盘,起点在右上角,两个人每轮可把棋子向左.向下或者向左下移动一格,直到不能移动棋子者输. NP图解决: 概念: 必败点(P点):前一个选手将取胜的位置称为必败点. 必胜点(N点):下一个选手将取胜的位置成为必胜点. 性质: 步骤: NP图: AC code: 1 #include <bits/stdc++.h> 2 #define inc(i, j, k) for(int i = j; i <= k; i++) 3 #def

前端笔试题目小结--获取输入参数用户名;查询URL字符串参数

编写一个JavaScript函数getSuffix,用于获得输入参数的后缀名.如输入abc.txt,返回txt. 1 str1 = "abc.txt"; 2 function getSuffix(str) 3 { 4 var index =str.indexOf("."); 5 if(index !=-1) 6 { 7 str = str.substring(index+1); 8 } 9 else{ 10 str = "not find"; 1

排序计数类题目小结

考虑到每次出哒哒哒操作,最少多少次操作使数组有序这一类题我都错的很惨,小小地总结一下 1.交换相邻位置的点->求逆序对(火柴排队,10.30noip模拟赛T1),有效的操作一定使得逆序对减少一个 2.交换任意位置->转化为置换(10.30noip模拟赛T1),置换内交换只需要置换中点个数次 3.每次可以把一个数扔到最前面->(HDU5500,noip模拟赛11.12T1)官方题解: 把这题的模型简化一下,有一个1→n的排列形成的数列,我们要用最少的操作次数把这个数列排序,每次操作都是把一

关于360笔试部分题目小结

先看看笔试的两道编程题:第一道完全没有思路,哪位有思路的,麻烦写出来,给我留言哈! 编程题一: 小B最近迷上了字符串处理技术,他设计了各种处理方式,并计算字符串的属性.这次也不例外,他定义了一种新的字符置换方式.小B研究的字符串由ASCII码字母和“.”构成,这次的研究对象是“.”.他关心的对象是字符串中出现的连续两个“.”.若每次操作把其中最开始的连续两个“.”以一个“”替代,则可以将函数f(s) 定义为使得串中不出现连续两个“.”的最小置换次数. 现考虑m个字符替换操作,每次将指定位置的字符

linux题目小结(一)

1定义一个别名对所有用户生效 [[email protected] ~]# cat /etc/bashrc ... alias actp='cat /etc/fstab' [[email protected] ~]$ source /etc/bashrc [[email protected] ~]# alias alias actp='cat /etc/fstab' [[email protected] ~]# su - lxq 上一次登录:三 7月 17 18:02:37 CST 2019:0