[题解]牛客练习赛24
好暴力,但ak了还是很开心.
A-石子阵列 组合数学
m(1000)种石子,每种无限个,从这些石子中取出n(1000)个,并按顺序排列起来,为了好看,相邻的石子不能相同。有多少种排列的方法,结果模1e9+7.
第一个石子有m种取法,第2~n个的限制是不能与前一个相同,有m-1种取法.
答案是$m*(m-1)^{n-1} \bmod 1000000007$
复杂度O(n),可以用快速幂优化.
int main(void) { ll n=read()-1, m=read(); ll ans = m; while(n--) ans = ans * (m-1) % MOD; cout << ans <<endl; return 0; }
B-凤凰 树
给定一棵树,每个节点上有一只鸟,每秒钟每只鸟都可以从一个节点移动到相邻节点上,但每秒钟每条边只能承载一只鸟的飞行.问所有鸟移动到根节点(1号节点)所花的时间.
假设根节点只有一个孩子,那么答案就是总结点个数-1,也即这个以这个孩子为根的子树的节点个数,原因是这个子树与根节点只有一条边相连,每秒钟最多走一个,且每秒钟都可以走一个,因为每秒钟总会有至少一只鸟在子树的根处等待.
假设根节点有若干个孩子,可以发现以这些孩子为根的子树之间是互不影响的,所以答案就是以根节点的孩子为根的子树中最多的节点数目.
复杂度O(n)
vector<int> save[M]; int getnode(int n, int fa) { int ret = 1; for(auto x : save[n]) if(x != fa) ret += getnode(x, n); return ret; } int main(void) { int n = read(); for(int i = 1; i < n; i++) { int a = read(), b = read(); save[a].push_back(b); save[b].push_back(a); } int ans = 0; for(auto x : save[1]) ans = max(ans, getnode(x, 1)); printf("%d\n", ans ); return 0; }
C-PH试纸 预处理答案
有一个长为n(1e6)的RB串,m(1e6)次询问,每次问第q个R或B字符出现在串中的第几个位置.
预先统计出所有的答案,对每个问题直接输出.复杂度O(n+m)
int rs[M], bs[M]; char tic[M]; int main(void) { int n = read(), m = read(); scanf("%s", tic); int rn = 0, bn = 0; for(int i = 0; tic[i]; i++) if(tic[i] == 'R') rs[++rn] = i + 1; else bs[++bn] = i + 1; char op[3]; int t; while(m--) { scanf("%s %d", op, &t); if(op[0] == 'R') printf("%d\n", t <= rn ? rs[t] : -1 ); else printf("%d\n", t <= bn ? bs[t] : -1 ); } return 0; }
D-插排树 树
一个n个节点的树,给出除根节点外所有节点的父亲及与父亲相连的边长,问所有节点距离根节点最远能有多远?
记忆化搜索一下,注意根节点不一定是1号点.复杂度O(n)
struct node { int len; int all = -1; int fa = -1; } save[M]; int getall(int id) { return save[id].all == -1 ? save[id].all = save[id].len + getall(save[id].fa) : save[id].all; } int main(void) { int n = read(); for(int i = 1; i < n; i++) { int id = read(), fa = read(), len = read(); save[id].fa = fa; save[id].len = len; } for(int i = 1; i <= n; i++) if(save[i].fa == -1) save[i].all = 0; int ans = 0; for(int i = 1; i <= n; i++) { if(save[i].all == -1) getall(i); ans = max(ans, save[i].all); } printf("%d\n", ans ); return 0; }
E-青蛙 bfs
有一条路,路上有很多两个点之间的单向隧道,在每一个点可以选择:朝前走一个点,朝后走一个点,进入一个隧道(如果当前点有)并从隧道另一边出来,每个选择都花费一秒时间.问至少需要多少时间可以从起点走到终点.
bfs,dis[i]表示从起点最少走多少步可以走到这个点,用dis[i]是否为-1来表示是否访问过这个点.
因为一定能到达终点,每次循环时拿dis[m]是否为-1作为判断条件.
int dis[M]; pair<int,int> trap[M]; int main(void) { int m = read(), n = read(); memset(dis, -1, sizeof(dis)); dis[0] = 0, dis[1] = 1; for(int i = 0; i < n; i++) { trap[i].first = read(); trap[i].second = read(); } queue<int> q; q.push(1); while(dis[m] == -1) { int now = q.front(); q.pop(); for(int i = 0; i < n; i++) { if(trap[i].first == now && dis[trap[i].second] == -1) { dis[trap[i].second] = dis[now] + 1; q.push(trap[i].second); } } if(dis[now - 1] == -1) { dis[now - 1] = dis[now] + 1; q.push(now - 1); } if(dis[now + 1] == -1) { dis[now + 1] = dis[now] + 1; q.push(now + 1); } } printf("%d\n", dis[m] ); return 0; }
F-三轮 完全背包(非正解)
有n(50000)种商品,每种都有一个体积vi(50000),每种个数无限,给定一个m(50000),问组成1~m体积的方法总数.
遇事不决写暴力
完全背包求方案数,复杂度O(nm),牛客的评测机是真的好
int save[M], dp[M]; int main(void) { int n = read(), m = read(); for(int i = 1; i <= n; i++) save[i] = read(); dp[0] = 1; for(int i = 1; i <= n; i++) for(int j = save[i]; j <= m; j++) dp[j] = (dp[j - save[i]] + dp[j]) % MOD; int ans = 0; for(int i = 1; i <= m; i++) ans = (ans + dp[i]) % MOD; printf("%d\n", ans ); return 0; }