Codeforces Round #411(Div. 2)——ABCDEF
30min水掉前面4T,30min尝试读懂EF题,60min划水
顺便D忘记取膜丢50分,距比赛结束10s时hack失败丢50分...
从2620掉分到2520,从rank227掉到rank354...血的教训
真吉尔丢人,题目戳这里
A. [L,R]中出现次数最多的因子个数
显然L != R 答案就是2,否则就是 R
B.仅用'a' 'b' 'c'造字符串,保证不能出现长度为3的回文子串,并尽量少用'c'
脑补一下发现直接aabbaabbaabb...这样子构造就可以满足了
C.n个地点标号1-n,从 i 到 j 的花费为 (i + j) % (n + 1)
出发地点和结束地点自己定,遍历全部n个地点的最小花费
我们如果直接 1 -> n , 2 -> (n - 1) ...这样子走每次花费就都是0
然后我们想办法连接他们,发现可以 1 -> n -> 2 -> (n - 1) -> 3 -> (n - 2) ...
所以answer = (n - 1) / 2
D.给个只包含'a''b'的字符串,遇'ab'就要换成'bba',求问到不能变换为止的最少变换次数
我们可以发现
1.变换后'a'的个数不变,b的个数1个变2个
2.最后字符串形式一定为bb...bbaa...aa
于是我们从后往前记录'b'的个数cnt,若遇到一个'a',那么它后面的'b'的个数
即这个'a'引起的需要变换的次数,即ans += cnt, 且经过cnt次变换后,'b'的个数变成了cnt * 2个
然后一直往前边扫边算就好了
#include <bits/stdc++.h> using namespace std; char s[1000010]; int main() { int n, mod_ = 1e9 + 7; long long m = 0, ans = 0; scanf("%s", s); for(int i = strlen(s) - 1;i >= 0;i --) { if(s[i] == 'b') m ++; else ans += m, ans %= mod_, m = m * 2 % mod_; } cout << ans; return 0; }
E.给一棵n个点的树,树上每个节点都有一个集合,每个集合中包含若干种cream
由m种cream构成的无向图G,若cream_u与cream_v之间有边
当且仅当树上存在一点,cream_u与cream_v同时存在该点的集合中
然后对图G的节点进行染色,使得任意一边连接的两点不同色
输出最少颜色数和任一方案
令一重要条件:任一cream存在的树上节点都构成一个联通子图,即
Vertices which have the i-th (1 ≤ i ≤ m) type of ice cream form a connected subgraph.
做题时感觉这棵树存在意义不大...是因为没有明白这个条件的用法
我们来直观的考虑一下这个条件,如果cream_i 存在于节点u的父亲节点中
却没有存在于u中,那么显然cream_i 不会再存在于以u为根节点的这个子树中了
所以我们就可以做了,从根节点开始 dfs,对于当前节点的集合中的所有cream
如果cream_i 在父亲节点中存在,那么它的颜色号就被标记为不可用
若不存在,那么就从 1 开始找可用的颜色号给它用就好了
一个小坑:可能有没有在树上出现过的cream,直接给它染成颜色1就好了
#include <cstdio> #include <vector> using namespace std; const int maxn = 300010; int n, m, k, d[maxn], g[maxn]; vector <int> e[maxn], f[maxn]; void dfs(int u, int fa) { int i, j = 1; for(i = 0;i < e[u].size();i ++) if(d[e[u][i]]) g[d[e[u][i]]] = 1; for(i = 0;i < e[u].size();i ++) if(!d[e[u][i]]) { while(g[j]) j ++; d[e[u][i]] = j; k = max(k, j); j ++; } for(i = 0;i < e[u].size();i ++) g[d[e[u][i]]] = 0; for(i = 0;i < f[u].size();i ++) if(f[u][i] != fa) dfs(f[u][i], u); } int main() { int u, v; scanf("%d %d", &n, &m); for(int i = 1;i <= n;i ++) { scanf("%d", &k); for(int j = 1;j <= k;j ++) scanf("%d", &u), e[i].push_back(u); } for(int i = 1;i < n;i ++) { scanf("%d %d", &u, &v); f[u].push_back(v); f[v].push_back(u); } k = 1, dfs(1, -1); printf("%d\n", k); for(int i = 1;i <= m;i ++) if(!d[i]) d[i] = 1; for(int i = 1;i <= m;i ++) printf("%d ", d[i]); return 0; }
F.给你一个森林,然后给你 q 组询问,每组询问包含 u 和 v
若在节点 u 所在的树和节点 v 所在的树之间随机加一条边的话,求新树的直径的期望值
(q神给的)解题思路:
如果只有一组,那么经过一开始的O(n)预处理后,我们可以O(min(siz[u], siz[v]))的时间求出来的
(如果能想到这个复杂度的话,可以跳过下面2段)
首先说明我们的预处理,包括每棵树的siz,直径,每个节点所在的树的编号
以及 len[i] 表示点 i 距离它所在的树中最远点的距离,显然有len[i] < siz[i] ,即肯定小于所在树的siz
然后预处理出关于len的权值前缀和之类,预处理基本结束
然后考虑只有一组询问的话,两棵树加一条边成一棵树
新的直径要么是原来一棵树的直径,要么是连接的两个点的 len 之和 + 1
即 old_d = max(du, dv), new_d = max(len[u] + len[v] + 1, old_d,)
我们只要处理出有多少组 len[u] + len[v] + 1> old_d,这些组都算前者,其余都算后者就好了
这时候就可以用到我们预处理出的权值前缀和了
对于确定的len[u],可以O(1)求出另一棵树中len[v] > old_d - len[u] - 1的 len[v] 之和
所以就可以做到O(min(siz[u], siz[v]))了
那么一组解决了,q组呢,用我们的比格思茅大法来证明复杂度!
首先令 siz_k = sqrt(n)
如果 u 和 v 所在的树有一棵树的siz < siz_k,即存在为思茅的话
那么这次询问的时间就是不超过O(sqrt(n))的
那么如果两颗树的 siz 均大于呢,即均为比格的话
因为上述算法取决于 siz 小的树的 siz
那么考虑最坏情况,两颗树 siz 相同且均大于 siz_k
那么如果查询所有不同的pair效率是多少呢, (n / siz) ^ 2 * siz
即n ^ 2 / siz,由于 siz > sqrt(n),所以 最坏O(n * sqrt(n))
当然为了避免同一pair重复查询可以用map来记忆化一下
综上,考虑map因素,时间O(n * sqrt(n) * logn)
然而实际表现还是相当不错的!
#include <bits/stdc++.h> #define pb push_back #define rep(i, j, k) for(int i = j;i < (k + 1);i ++) using namespace std; typedef long long ll; const int maxn = 100010; bool vis[maxn]; int n, m, q, ks, st, dis; int cnt, cot, zx[maxn], siz[maxn], tmp[maxn], len[maxn], dlen[maxn]; vector <int> e[maxn]; vector <ll> f2[maxn], f1[maxn], f3[maxn]; struct node { int x, y; bool operator < (const node &a) const { if(x == a.x) return y < a.y; return x < a.x; } }; map <node, double> p; void dfs1(int x, int f, int d) { int y; vis[x] = 1, cot ++; if(d > dis) dis = d, st = x; rep(i, 0, e[x].size() - 1) { y = e[x][i]; if(y == f) continue; zx[y] = zx[x]; dfs1(y, x, d + 1); } } void dfs2(int x, int f, int d) { int y; len[x] = max(len[x], d); if(d > dis) dis = d, st = x; rep(i, 0, e[x].size() - 1) { y = e[x][i]; if(y != f) dfs2(y, x, d + 1); } } void dfs3(int x, int f) { int y; tmp[len[x]] ++; rep(i, 0, e[x].size() - 1) { y = e[x][i]; if(y != f) dfs3(y, x); } } int main() { ll s; node temp; int u, v, w, t; double ans1, ans2; ios::sync_with_stdio(false); cin >> n >> m >> q; ks = (int)sqrt(n + 0.5); rep(i, 1, m) { cin >> u >> v; e[u].pb(v), e[v].pb(u); } rep(i, 1, n) if(!vis[i]) { cot = 0; zx[i] = ++cnt; dis = -1, dfs1(i, i, 0), siz[cnt] = cot; dis = -1, dfs2(st, st, 0), dlen[cnt] = dis; dis = -1, dfs2(st, st, 0); dfs3(i, i); rep(j, 0, dlen[cnt]) f3[cnt].pb(tmp[j]);s = 0; rep(j, 0, dlen[cnt]) f1[cnt].pb(s += tmp[j]);s = 0; rep(j, 0, dlen[cnt]) f2[cnt].pb(s += 1ll * j * tmp[j]); rep(j, 0, dlen[cnt]) tmp[j] = 0; } rep(i, 1, q) { cin >> u >> v; if(zx[u] == zx[v]) puts("-1"); else { ans1 = ans2 = 0; u = zx[u], v = zx[v]; if(siz[u] > siz[v]) swap(u, v); if(siz[u] <= ks) { w = max(dlen[u], dlen[v]); rep(j, 0, dlen[u]) { t = w - j - 1; if(t < 0) ans1 += f3[u][j] * (f2[v][dlen[v]] + f1[v][dlen[v]] * (1 + j)); else if(t > dlen[v]) ans1 += f3[u][j] * (f1[v][dlen[v]] * w); else ans1 += f3[u][j] * (f2[v][dlen[v]] - f2[v][t] + (f1[v][dlen[v]] - f1[v][t]) * (1 + j) + f1[v][t] * w); } ans2 = 1.0 * siz[u] * siz[v]; printf("%.10f\n", ans1 / ans2); } else { if(p[(node){u, v}]) printf("%.10f\n", p[(node){u, v}]); else { w = max(dlen[u], dlen[v]); rep(j, 0, dlen[u]) { t = w - j - 1; if(t < 0) ans1 += f3[u][j] * (f2[v][dlen[v]] + f1[v][dlen[v]] * (1 + j)); else if(t > dlen[v]) ans1 += f3[u][j] * (f1[v][dlen[v]] * w); else ans1 += f3[u][j] * (f2[v][dlen[v]] - f2[v][t] + (f1[v][dlen[v]] - f1[v][t]) * (1 + j) + f1[v][t] * w); } ans2 = 1.0 * siz[u] * siz[v]; printf("%.10f\n", ans1 / ans2); p[(node){u, v}] = ans1 / ans2; } } } } }
代码有点丑,半夜撸了2h的代码非常狗
下午又调了1h才调出来,谨慎参考,希望能有所帮助