题意:
n 个人分 m 元的红包,然后告诉你 k 个人获得的红包大小,问你如果你想成为手气王(钱拿的最多),至少需要得到多大的红包,或者输出”Impossible”,如果不可能的话,注意红包必须分完,并且每个人获得的红包大小都是整数 (>0)
分析:
首先可以得到 k 个人里的最大值 M ,设现在剩下的钱是 R ,那么首先要成为手气王,就必须得到至少 M+1 ,但是最多是 R−(n−k−1) , 那么接下来其实就可以二分来处理了,假设现在的答案是mid,考虑如果在那 n−k−1 人里有人同样得到了mid元,即不等式 2⋅mid+n−k−2≤R ,那么mid这个答案是非法的,否则就是合法的,如此就可以二分 得到最小的答案了,注意特判 k=n−1 的情况,因为红包必须分完。
题意:
给出一个 n⋅m 的二维格子,要求使用不超过 k 种的颜色进行染色,其中要求,曼哈顿距离为奇数的格子不能使用相同的颜色,求方案数, Mod 109+7
分析:
首先对格子进行黑白染色,那么任意一对黑白格子之间的距离都是奇数,所以染黑色格子的颜色和染白色格子的颜色不能相同。
解法1:
先dp预处理, dp[i][j] 表示刚好用 i 种颜色,染完 j 个格子的方案数
dp[i][j]=ij−C(i,i−1)⋅dp[i−1][j]−C(i,i−2)⋅dp[i−2][j]−...−C(i,1)⋅dp[1][j]
然后对于给定的 n,m,k ,计算出黑白格子的数目,然后枚举一共使用多少种颜色(设为 i ),再枚举使用多少种颜色给黑格子染色(设为 j ),答案就是 C(k,i)⋅C(i,j)⋅dp[j][black]⋅dp[i−j][white]
时间复杂度 O(T⋅k2)
解法2:
其实和解法1是类似的,同样也是对于给定的 n,m,k ,计算出黑白格子数目,枚举一共使用多少种颜色(设为 i ),再枚举使用多少种颜色给黑格子染色(设为 j ),根据容斥原理,此时使用 j 种颜色给黑格子染色的方案数为:
C(j,j)⋅jblack−C(j,j−1)⋅(j−1)black+C(j,j−2)⋅(j−2)black−...+(−1)j−1⋅C(j,1)
同理可以算出此时使用 i−j 种颜色对白格子染色的方案数,二者相乘再乘上 C(k,i) 就是此时的答案,累加即可。
题意:
给定一棵树,树上每个结点都是一个小写字母
从1出发到n个结点有n条简单路径,每条路径求出所构成的字符串的不同子串数目
分析:
如果我们能对一个字符串,结尾进行添加删除,并且查询子串,那么树这个条件就没有卵用了,主要从树根dfs下去,到某一结点就相当于在结尾添加字符,递归回去的时候就相当于删除字符。
解法:
通过上面的分析问题就转化为如何在一个字符串末尾添加删除字符,并且能统计子串数目。
回忆一下用后缀数组处理这个问题的统计方法为 ∑n1n−sa[i]−lcp(sa[i],sa[i−1])
那么把字符串逆过来,删除和添加就是在头部添加,这样的话,每次添加删除就相当于添加删除一个后缀串,如果能快速找到这个后缀串的sa,只要在插入删除的时候和前面后面串简单计算一下,维护一个子串数目就可以了。
这步可以用一个set来进行维护,那么如何比较两个串的字典序大小呢,只要把字符串进行hash,两个串比较一下LCP之后第一个字符就可以了。
时间复杂度为 O(nlog2(n))
题意:
给出一颗无权树,求最远点对数目。
分析:
非常抱歉题目没有讲清楚,点对 (a,b) 中 a 可以等于 b
暂时没有标程的解法,下面讲下我自己的做法。
首先通过两次 bfs 求出直径长度 D 。
接下来考虑深搜这棵树(计算某个结点时,相当于考虑这个结点作为某个最远点对的LCA),假设在处理某个以 u 为根的子树, u 的儿子已经全部处理好,我们需要知道 u 的每个儿子的子树里深度最大的叶子节点深度和对应数量,然后同时可以把这个信息更新给 u , 接下来依次枚举 u 的每个儿子,假设现在处理 v , 设 v 这个子树里距离 v 最远的点的深度为 d ,数量为 c ,设 u 深度为 du ,则现在要找深度为 D−(d−du)+du 的点,而每当我们处理好一个儿子,就把距离它最远的点的深度以及数目存起来了,因此此时我就可以得到深度为 D−(d−du)+du 的点的数目,每次累加即可,注意需要清空答案。另外 n=1 答案是1。。。
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
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;
}