Good Bye 2015 (One University One Question) Round III

A. Red packet

题意:
n 个人分m元的红包,然后告诉你 k 个人获得的红包大小,问你如果你想成为手气王(钱拿的最多),至少需要得到多大的红包,或者输出”Impossible”,如果不可能的话,注意红包必须分完,并且每个人获得的红包大小都是整数(>0)

分析:
首先可以得到 k 个人里的最大值M,设现在剩下的钱是 R ,那么首先要成为手气王,就必须得到至少M+1,但是最多是 R(nk1) , 那么接下来其实就可以二分来处理了,假设现在的答案是mid,考虑如果在那 nk1 人里有人同样得到了mid元,即不等式 2mid+nk2R ,那么mid这个答案是非法的,否则就是合法的,如此就可以二分 得到最小的答案了,注意特判 k=n1 的情况,因为红包必须分完。

B. Coloring

题意:
给出一个 nm 的二维格子,要求使用不超过 k 种的颜色进行染色,其中要求,曼哈顿距离为奇数的格子不能使用相同的颜色,求方案数,Mod 109+7

分析:
首先对格子进行黑白染色,那么任意一对黑白格子之间的距离都是奇数,所以染黑色格子的颜色和染白色格子的颜色不能相同。

解法1:

先dp预处理, dp[i][j] 表示刚好用 i 种颜色,染完j个格子的方案数
dp[i][j]=ijC(i,i1)dp[i1][j]C(i,i2)dp[i2][j]...C(i,1)dp[1][j]

然后对于给定的 n,m,k ,计算出黑白格子的数目,然后枚举一共使用多少种颜色(设为 i ),再枚举使用多少种颜色给黑格子染色(设为j),答案就是 C(k,i)C(i,j)dp[j][black]dp[ij][white]

时间复杂度 O(Tk2)

解法2:
其实和解法1是类似的,同样也是对于给定的 n,m,k ,计算出黑白格子数目,枚举一共使用多少种颜色(设为 i ),再枚举使用多少种颜色给黑格子染色(设为j),根据容斥原理,此时使用 j 种颜色给黑格子染色的方案数为:
C(j,j)jblackC(j,j1)(j1)black+C(j,j2)(j2)black...+(1)j1C(j,1)
同理可以算出此时使用 ij 种颜色对白格子染色的方案数,二者相乘再乘上 C(k,i) 就是此时的答案,累加即可。

C. String in the tree

题意:
给定一棵树,树上每个结点都是一个小写字母
从1出发到n个结点有n条简单路径,每条路径求出所构成的字符串的不同子串数目
分析:
如果我们能对一个字符串,结尾进行添加删除,并且查询子串,那么树这个条件就没有卵用了,主要从树根dfs下去,到某一结点就相当于在结尾添加字符,递归回去的时候就相当于删除字符。

解法:
通过上面的分析问题就转化为如何在一个字符串末尾添加删除字符,并且能统计子串数目。
回忆一下用后缀数组处理这个问题的统计方法为 n1nsa[i]lcp(sa[i],sa[i1])
那么把字符串逆过来,删除和添加就是在头部添加,这样的话,每次添加删除就相当于添加删除一个后缀串,如果能快速找到这个后缀串的sa,只要在插入删除的时候和前面后面串简单计算一下,维护一个子串数目就可以了。
这步可以用一个set来进行维护,那么如何比较两个串的字典序大小呢,只要把字符串进行hash,两个串比较一下LCP之后第一个字符就可以了。
时间复杂度为 O(nlog2(n))

D. Ancient battle tree

题意:

给出一颗无权树,求最远点对数目。

分析:
非常抱歉题目没有讲清楚,点对 (a,b) a 可以等于b
暂时没有标程的解法,下面讲下我自己的做法。
首先通过两次 bfs 求出直径长度 D
接下来考虑深搜这棵树(计算某个结点时,相当于考虑这个结点作为某个最远点对的LCA),假设在处理某个以u为根的子树, u 的儿子已经全部处理好,我们需要知道u的每个儿子的子树里深度最大的叶子节点深度和对应数量,然后同时可以把这个信息更新给 u , 接下来依次枚举u的每个儿子,假设现在处理 v , 设v 这个子树里距离 v 最远的点的深度为d,数量为 c ,设u深度为 du ,则现在要找深度为 D(ddu)+du 的点,而每当我们处理好一个儿子,就把距离它最远的点的深度以及数目存起来了,因此此时我就可以得到深度为 D(ddu)+du 的点的数目,每次累加即可,注意需要清空答案。另外 n=1 答案是1。。。


#include <iostream>
#include <fstream>
#include <cstring>
#include <climits>
#include <deque>
#include <cmath>
#include <queue>
#include <stack>
#include <list>
#include <map>
#include <set>
#include <utility>
#include <sstream>
#include <complex>
#include <string>
#include <vector>
#include <cstdlib>
#include <cstdio>
#include <ctime>
#include <bitset>
#include <functional>
#include <algorithm>

using namespace std;

const int N = 200010;
int deep[N];
int tar[N];
int cnt[N];
int head[N];
int tot;
bool vis[N];
int dist[N];
struct Edge {
    int next;
    int to;
}edge[N << 1];
int end_p;

void addedge(int from, int to){
    edge[tot].to = to;
    edge[tot].next = head[from];
    head[from] = tot++;
}

int bfs(int s) {
    queue <int> qu;
    memset(vis, 0, sizeof(vis));
    memset(dist, 0x3f3f3f3f, sizeof(dist));
    int d = 0;
    qu.push(s);
    dist[s] = 0;
    vis[s] = 1;
    while (!qu.empty()) {
        int u = qu.front();
        qu.pop();
        for (int i = head[u]; ~i; i = edge[i].next) {
            int v = edge[i].to;
            if (!vis[v]) {
                vis[v] = 1;
                qu.push(v);
                dist[v] = dist[u] + 1;
                if (d < dist[v]) {
                    d = dist[v];
                    end_p = v;
                }
            }
        }
    }
    return d;
}

long long ans = 0;
int max_d;

int use[N];
int cases = 0;
void dfs(int u, int fa, int d) {
    deep[u] = d;    
    bool flag = 0;
    tar[u] = -1;
    for (int i = head[u]; ~i; i = edge[i].next) {
        int v = edge[i].to;
        if (v == fa) {
            continue;
        }
        flag = 1;
        dfs(v, u, d + 1);
        tar[u] = max(tar[u], tar[v]);
    }
    if (!flag) {
        cnt[u] = 1; // leaf node
        tar[u] = deep[u];
        return;
    }
    for (int i = head[u]; ~i; i = edge[i].next) {
        int v = edge[i].to;
        if (v == fa) {
            continue;
        }
        if (tar[u] == tar[v]) {
            cnt[u] += cnt[v];
        }
        int res = max_d - (tar[v] - deep[u]);
        if (res == 0) {
            ans += cnt[tar[v]];
        }
        else {
            ans += (long long)use[deep[u] + res] * cnt[v];
        }
        use[tar[v]] += cnt[v];
    }
    for (int i = head[u]; ~i; i = edge[i].next) {
        int v = edge[i].to;
        if (v == fa) {
            continue;
        }
        use[tar[v]] = 0;
    }
}

int main() {
    int n, u, v;
    while (~scanf("%d", &n)) {
        ++cases;
        if (n == 1) {
            printf("1\n");
            continue;
        }
        memset(head, -1, sizeof(head));
        memset(cnt, 0, sizeof(cnt));
        tot = 0;
        memset(use, 0, sizeof(use));
        for (int i = 1; i <= n - 1; ++i) {
            scanf("%d%d", &u, &v);
            addedge(u, v);
            addedge(v, u);
        }
        bfs(1);
        max_d = bfs(end_p);
        ans = 0;
        if (max_d == n - 1) {
            printf("1\n");
            continue;
        }
        dfs(1, -1, 1);
        printf("%lld\n", ans);
    }
    return 0;
}
  • 2
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值