LGR-143-解题
目录
A. 三个数
DESCRIPTION
- 有一个有 个数的集合 。
- 要求构造一个只包含非负整数的集合(无重复元素),使得 里面的任何一个数都能被这个集合里面大于等于 个不同的数相加得到,求这个集合中至少包含多少个元素。
SOLTION
- 对于 是,可以发现, 有且只可以被 表示出来。
- 推广到更大的情况, 最大可以表示为 ,但是 无法表达出 来。
以此类推,下一个数一定是前面数的和,即前一个数的两倍。
正确性类似与,倍增的写法。
判断:将每个集合的和 纪录下来,使得 一定能被表示出来满足 ,时间复杂度为 。
CODE
#include <bits/stdc++.h>
using namespace std;
#define int long long
const int K = 43;
int c[1001000], t, w, tmp;
signed main() {
c[1] = 0; c[2] = 1; c[3] = 2; c[4] = 3;
for (int i = 5; i < K; i ++ ) {
c[i] = c[i - 1] << 1;
}
for (int i = 0; i < K; i ++ ) {
c[i] = c[i - 1] + c[i];
}
scanf("%lld",&t);
while (t -- ) {
scanf("%lld",&w);
tmp = lower_bound(c, c + K, w) - c;
cout << tmp << '\n';
}
return 0;
}
B. 一些数
DESCRIPTION
- 钦定一些位置的值,问有多少长度为 的排列满足条件,且最长上升序列的长度为 .
SOLTION
- 最长上升子序列的长度为 ,就相当于将这个数列 的一个数重新插入到序列的其他位置所得的的序列。
对于给出的 与 ,大型分类讨论的第 题!。。。
-
如果所有 ,就说明其中的改变,仅存在与每个 之间,这样的贡献为 。
-
如果出现 ,那么这样的序列尤其最多只会存在一个,暴力 判断即可。
- 对于相邻的两个数 交换的情况,也是相同的。
-
如果仅出现一段连续的 ,那么答案为 ;
-
是将,这段连续的区间的开头 与它前面的 的 之间选择一个数 ,共 种。
-
在这段的结尾 ,与它后面的 的之间的间隔中选择一个,共 种。
-
-
相对应的,如果仅出现一段连续的 ,那么答案为 ,同上。
CODE
// jiji...
#include <bits/stdc++.h>
using namespace std;
#define int long long
const int KI = 1e6 + 6;
inline int read() {
int x = 0; bool f = false; char c = getchar();
while (!isdigit(c)) {f |= (c == '-'); c = getchar();}
while (isdigit(c)) {x = (x << 1) + (x << 3) + (c ^ 48); c = getchar();}
return f ? -x : x;
}
int n, q, ans, x[KI], y[KI], k[KI], l, r, tmp, st, ed;
bool flag1, flag2, flag3;
int ksm(int a, int k) {
int ans = 1;
while (k) {
if (k & 1) ans = ans * a;
a = a *a; k >>= 1;
}
return ans;
}
bool check() {
int fvr = 0;
for (int i = 1; i <= q; i ++ ) {
if (abs(x[i] - y[i]) > 1) {
if (!fvr) fvr = i;
else return false;
}
}
if (fvr) {
bool f1 = false, f2 = false;
if (x[fvr] > y[fvr]) {
for (int i = 1; i <= n; i ++ ) {
if (!k[i]) continue;
if (i >= y[fvr] && i < x[fvr] && k[i] != i + 1) return false;
if ((i < y[fvr] || i > x[fvr]) && k[i] != i) return false;
}
}
else {
for (int i = 1; i <= n; i ++ ) {
if (!k[i]) continue;
if ((i > y[fvr] || i < x[fvr]) && k[i] != i) return false;
if ((i <= y[fvr] && i > x[fvr]) && k[i] != i - 1) return false;
}
}
return true;
}
else {
flag2 = false;
for (int i = 1; i <= n; i ++ ) {
if (!k[i] || k[i] == i) continue;
if (k[i] == i + 1 && k[i + 1] == i) {
if (flag2 == false) flag2 = true;
else return false;
}
}
return flag2;
}
}
void sol() {
n = read(); q = read();
for (int i = 1; i <= n; i ++ ) k[i] = 0;
flag1 = flag2 = false;
for (int i = 1; i <= q; i ++ ) {
x[i] = read(); y[i] = read();
k[x[i]] = y[i];
if (x[i] != y[i]) flag1 = true;
}
if (flag1 == false) {
ans = 1;
for (int i = 1; i <= q; i ++ ) {
ans = ans * ksm(x[i] - x[i - 1] - 2, 2);
}
ans = ans * ksm(n + 1 - x[q] - 2, 2);
cout << ans << '\n';
return ;
}
if (check() == true) cout << 1 << '\n';
else {
flag2 = flag3 = false;
l = INT_MAX; r = INT_MIN;
tmp = st = 0; ed = n + 1;
for (int i = 1; i <= n; i ++ ) {
if (!k[i]) continue;
if (k[i] == i) {
if (flag2 == false) tmp = k[i];
else if (flag2 && !flag3) {ed = k[i]; flag3 = true;}
}
else if (k[i] - 1 == i) {
if (flag2 && flag3) {
cout << 0 << '\n';
return ;
}
l = min(l, k[i]); r = max(r, k[i]);
flag2 = true; st = tmp;
}
}
if (flag2 == true) {
cout << (l - st) * (ed - r - 1) << '\n';
return ;
}
else {
flag2 = flag3 = false;
tmp = st = 0; ed = n + 1;
for (int i = 1; i <= n; i ++ ) {
if (!k[i]) continue;
if (k[i] == i) {
if (flag2 == false) tmp = k[i];
else if (flag2 && !flag3) {ed = k[i]; flag3 = true;}
}
else if (k[i] + 1 == i) {
if (flag2 && flag3) {
cout << 0 << '\n';
return ;
}
l = min(l, k[i]); r = max(r, k[i]);
flag2 = true; st = tmp;
}
}
if (flag2) cout << (l - st - 1) * (ed - r) << '\n';
else cout << 0 << '\n';
return ;
}
}
}
signed main() {
int T = read();
while (T -- ) sol();
return 0;
}
C. 一棵树
DESCRIPTION
- 给定一颗树,每个点上有权值 。定义路径 的权值为 。
- 求 。
SOLTION
令 为 的位数(tip: 的位数为 )。
对于树退化到链。
考虑以 作为结尾时的贡献,设 为以 作为结尾, 的任意数作为开始时的贡献。
(由于 ,所以需要将链反过来再进行一遍计算)
在以 结尾的,并且以 的任意数作为开始的序列的序列共有 个,
那么有,
对于树到星图。
- 对于路径长度为 的,求 即可。
- 对于路径长度为 的,
- 以 为起点的,其中叶子节点的贡献为 ,节点 的贡献为 。
- 以 为终点的,其中叶子节点的贡献为 ,节点 的贡献为 。
- 对于路径长度为 的,
- 以 为终点,那么节点 的贡献为 ,节点 的贡献为 ,其他节点的贡献为 。
对于树的情况。
如同链的情况一样,设 为以 作为结尾, 子树里的任意数作为开始时的贡献。
那么有,
对于每一个点 来讲,求一遍是 的,可以换根做到 的时间复杂度。
CODE
#include <bits/stdc++.h>
using namespace std;
#define int long long
const int KI = 3e6 + 7;
const int mod = 998244353;
inline int read() {
int x = 0; bool f = false; char c = getchar();
while (!isdigit(c)) {f |= (c == '-'); c = getchar();}
while (isdigit(c)) {x = (x << 1) + (x << 3) + (c ^ 48); c = getchar();}
return f ? -x : x;
}
int k[KI], a[KI], n;
int dep[KI], siz[KI], tmpsiz, tmp, sum, rot, ans, f[KI];
int to[KI], hd[KI], nxt[KI], cnt;
int ksm(int a, int k) {
int ans = 1;
while (k) {
if (k & 1) ans = ans * a %mod;
a = a * a %mod; k >>= 1;
}
return ans;
}
/*************************************************/
void sol_chain() {
tmp = 0; ans = 0;
for (int i = 1; i <= n; i ++ ) {
tmp = ((tmp * ksm(10, k[i]) %mod) + (a[i] * i %mod)) %mod;
ans = (ans + tmp) %mod;
}
tmp = 0;
for (int i = n; i >= 1; i -- ) {
tmp = ((tmp * ksm(10, k[i]) %mod)+ (a[i] * (n - i + 1) %mod)) %mod;
ans = (ans + tmp) %mod;
}
for (int i = 1; i <= n; i ++ ) ans = ((ans - a[i]) %mod + mod) %mod;
cout << ans << '\n';
return ;
}
/*************************************************/
void sol_star() {
sum = 0; ans = 0;
for (int i = 1; i <= n; i ++ ) {
ans = (ans + (a[i] * n %mod)) %mod;
}
for (int i = 2; i <= n; i ++ ) {
ans = (ans + (ksm(10, k[1]) * a[i] %mod)) %mod;
ans = (ans + ((ksm(10, k[i]) * a[1] %mod) * (n - 1) %mod) ) %mod;
sum = (sum + ksm(10, k[i] + k[1])) %mod;
}
for (int i = 2; i <= n; i ++ ) {
tmp = ((sum - ksm(10, k[i] + k[1])) %mod + mod) %mod;
ans = (ans + (tmp * a[i] %mod)) %mod;
}
cout << ans << '\n';
return ;
}
/*************************************************/
void init(int u, int fa) {
siz[u] = 1;
int tmp = 0;
for (int i = hd[u]; i; i = nxt[i]) {
int v = to[i];
if (v == fa) continue;
init(v, u);
siz[u] += siz[v];
tmp = (tmp + f[v]) %mod;
}
f[u] = ((tmp * ksm(10, k[u]) %mod) + (a[u] * siz[u] %mod)) %mod;
}
void dfs(int u, int fa) {
int tmp = 0;
for (int i = hd[u]; i; i = nxt[i]) {
int v = to[i];
if (v == fa) continue;
tmp = (tmp + f[v]) %mod;
}
if (u != 1) {
f[u] = (((((f[fa]-f[u]*ksm(10, k[fa])%mod)%mod+mod)-a[fa]*siz[u]+tmp)%mod+mod)%mod * ksm(10, k[u]) %mod) + (a[u] * n) %mod;
}
for (int i = hd[u]; i; i = nxt[i]) {
int v = to[i];
if (v == fa) continue;
dfs(v, u);
}
}
void sol_tree() {
init(1, 0); dfs(1, 0);
for (int i = 1; i <= n; i ++ ) {
ans = (ans + f[i]) %mod;
}
cout << ans << '\n';
return ;
}
/*************************************************/
void addedge(int u, int v) {
to[++ cnt] = v;
nxt[cnt] = hd[u];
hd[u] = cnt;
return ;
}
signed main() {
n = read();
for (int i = 1, tmp; i <= n; i ++ ) {
tmp = a[i] = read();
if (!tmp) k[i] = 1;
while (tmp) {
k[i] ++; tmp /= 10;
}
}
for (int u = 2, v; u <= n; u ++ ) {
v = read();
addedge(u, v); addedge(v, u);
}
sol_tree();
return 0;
}
D. 好多数
DESCRIPTION
- 定义“ 号数学树”为一颗根节点权值为 的树,其中对于任意权值 的节点,其儿子的权值为 的因子 。
- 多次询问权值为 的节点数。
SOLTION
对于每个 将其写作 的形式(质因数分解)。
那么向上跳父亲的过程可以表示为:将某些 进行增大,直到所有的 。
设深度为 的 总共有 个,那么最终答案即为 。
对于每个因数,设 表示为跳了 次,值增加了 的方案数,利用插板法,得到
设 表示包含空选时深度为 的方案数,即
那么 即为 去掉空选的情况,容斥即可。
CODE
#include <bits/stdc++.h>
using namespace std;
#define int long long
const int N = 1e4 + 7, mod = 998244353;
int n, ans, cnt, x, q;
int jc[N], inv[N], f[N], a[N], b[N], c[N];
int ksm(int a, int k) {
int ans = 1;
while (k) {
if (k & 1) ans = ans * a %mod;
a = a * a %mod; k >>= 1;
}
return ans;
}
int C(int n, int m) {
return (jc[n] * inv[m] %mod) * inv[n - m] %mod;
}
signed main() {
jc[0] = 1;
for (int i = 1; i < N; i ++ ) jc[i] = (jc[i - 1] * i) %mod;
inv[N - 1] = ksm(jc[N - 1], mod - 2);
for (int i = N - 2; i >= 0; i -- ) {
inv[i] = (inv[i + 1] * (i + 1)) %mod;
}
n = 1;
while (scanf("%lld%lld",&a[n],&b[n]) != EOF) {
if (!a[n] && !b[n]) { n -- ; break;}
n ++;
}
scanf("%lld",&q);
while (q -- ) {
scanf("%lld",&x);
ans = cnt = 0;
if (x == 1) {
cout << 0 << ' '; continue;
}
for (int i = 1; i <= n; i ++ ) {
c[i] = b[i];
while (x % a[i] == 0 && c[i]) {
x /= a[i]; c[i] -- ;
}
cnt += c[i];
}
if (x > 1) {cout << 0 << ' '; continue;}
if (cnt == 0) {cout << 1 << ' '; continue;}
for (int i = 1; i <= cnt; i ++ ) {
f[i] = 1;
for (int j = 1; j <= n; j ++ ) {
if (c[j]) f[i] = f[i] * C(c[j] + i - 1, i - 1) %mod;
}
for (int j = 1; j < i; j ++ ) {
f[i] = ((f[i] - f[j] * C(i, j)) %mod + mod) %mod;
}
ans = (ans + f[i]) %mod;
}
cout << ans << ' ';
}
return 0;
}
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 地球OL攻略 —— 某应届生求职总结
· 周边上新:园子的第一款马克杯温暖上架
· Open-Sora 2.0 重磅开源!
· 提示词工程——AI应用必不可少的技术
· .NET周刊【3月第1期 2025-03-02】