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;
}
View Code

 

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;
 }
View Code

 

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;
                }
            }
        }
    }
}
View Code

代码有点丑,半夜撸了2h的代码非常狗

下午又调了1h才调出来,谨慎参考,希望能有所帮助

 

posted @ 2017-05-05 02:10  ztztyyy  阅读(213)  评论(0编辑  收藏  举报