CF396
CF396
CF396A
CF396A题意
给出一个长度为 \(n\) 的序列 \(a\),令 \(m=\prod_{i=1}^na_i\),问有多少个长度为 \(n\) 的序列使得序列中的所有数的乘积等于 \(m\)。
CF396A题解
首先质因数分解,可以证明,最多的质因数不会超过 \(500\times \log_210^9=14949\),于是可以快乐预处理。
然后对于每一个质因数,设它在原数组中有 \(k_i\) 个,那么相当于在 \(n\) 个有顺序的盒子中放入 \(k_i\) 个相同的小球,每个盒子可以不放或者放多个。很显然方案数是 \({n+k_i-1\choose n-1}\) 个。用不到 Lucas
定理。
CF396A代码
#include<bits/stdc++.h> #define int long long using namespace std; inline int read(){ int x = 0, f= 1;char ch = getchar(); while(ch < '0' || ch > '9'){if(ch == '-') f = -1;ch = getchar();} while(ch >= '0' && ch <= '9'){x = (x << 1) + (x << 3) + (ch ^ 48);ch = getchar();} return x * f; } const int maxn = 1e5 + 10, mod = 1e9 + 7; int n; int prime[maxn], tot; bool book[maxn]; unordered_map<int,int> mp; int pw[maxn], invpw[maxn]; int qpow(int x,int a){ int res = 1; while(a){ if(a & 1)res = res * x % mod; x = x * x % mod;a >>= 1; } return res; } int C(int n,int m){return pw[n] * invpw[m] % mod * invpw[n - m] % mod;} signed main(){ n = read();pw[0] = 1;pw[1] = 1; for(int i = 2;i <= 1e5;i++){ pw[i] = pw[i - 1] * i % mod; if(!book[i])prime[++tot] = i; for(int j = 1;j <= tot && prime[j] * i <= 1e5;j++){ book[i * prime[j]] = 1; if(i % prime[j] == 0)break; } } invpw[(int)1e5] = qpow(pw[(int)1e5],mod - 2); for(int i = 1e5 - 1;i + 1;i--){invpw[i] = invpw[i + 1] * (i + 1) % mod;} for(int i = 1;i <= n;i++){ int x = read(); for(int j = 1;j <= tot;j++) while(x % prime[j] == 0){ mp[prime[j]]++;x /= prime[j]; } if(x != 1)mp[x]++; } int ans = 1; for(auto it = mp.begin();it != mp.end();it++){ // printf("it = %lld\n",it->second); ans = ans * C(n + (it->second) - 1,n - 1) % mod; } printf("%lld\n",ans); return 0; }
CF396B
CF396B题意
我们设:
- \(v(n)\)是不超过\(n\)的最大素数;
- \(u(n)\)是大于\(n\)的最小素数。
计算$$\sum_{i=2}^n\frac{1}{u(n)\times v(n)}$$
其中 \(n\le10^9\)
CF396B题解
观察可得,对于相邻的质数 \(p_i,p_{i+1}\),\(\frac{1}{p_i\times p_{i+1}}\) 出现的次数是 \(p_{i+1}-p_i\),证明显然。
那么一对相邻的质数贡献就是 \(\frac{p_{i+1}-p_i}{p_i\times p_{i+1}}=\frac{1}{p_i}-\frac{1}{p_{i+1}}\)。
不难发现,将所有的式子写出来之后,只会剩下 \(\frac{1}{2}-\frac{1}{u(n)}\)。
不过上式必须保证每个分式的出现次数满足要求,但是 \([u(n),n]\) 这一段的分式出现次数不一定满足,怎么办?
先找到 \(u(n)\) 和 \(v(n)\),显然在 \([1,u(n))\) 的范围内上式满足,答案是 \(\frac{1}{2}-\frac{1}{u(n)}\),那我们要求的就是 \([u(n),n]\) 范围内的和。
这段的答案显然就是 \(\frac{n - u(n)+1}{u(n)\times v(n)}\),进行约分的最后答案就是 $$\frac{u(n)\times v(n) +2\times(n-u(n)-v(n)+1)}{u(n)\times v(n)}$$
什么?你问我 \(u(n),v(n)\) 怎么求?暴力算啊!
我不会告诉你这玩应我尝试找算法的QAQ
CF396B代码
#include<bits/stdc++.h> #define int long long using namespace std; inline int read(){ int x = 0, f= 1;char ch = getchar(); while(ch < '0' || ch > '9'){if(ch == '-') f = -1;ch = getchar();} while(ch >= '0' && ch <= '9'){x = (x << 1) + (x << 3) + (ch ^ 48);ch = getchar();} return x * f; } const int maxn = 1e5 + 10, mod = 1e9 + 7; int n; int prime[maxn], tot; bool book[maxn]; bool isprime(int x){ for(int i = 1, len = sqrt(x);i <= tot && prime[i] <= len;i++) if(x % prime[i] == 0)return false; return true; } int U(int x){ x++;while(!isprime(x))x++; return x; } int V(int x){ while(!isprime(x))x--; return x; } int gcd(int x,int y){return y == 0 ? x : gcd(y,x % y);} signed main(){ for(int i = 2;i <= 1e5;i++){ if(!book[i])prime[++tot] = i; for(int j = 1;j <= tot && prime[j] * i <= 1e5;j++){ book[i * prime[j]] = 1; if(i % prime[j] == 0)break; } } int T = read(); while(T--){ n = read(); int x = U(n), y = V(n); // printf("u = %lld, v = %lld\n", x, y); int u = x * y + (n - x - y + 1) * 2, v = 2 * x * y; int g = gcd(u, v); printf("%lld/%lld\n",u / g,v / g); } return 0; }
CF396C
CF396C题意
你在纸上画了一棵有n个点的有根树,树的根编号为1.
一开始每个节点上面的值都为0。
现在有q个询问,每个询问有两种类型:
- \(1\space v\space x\space k\)
你需要给 \(v\) 节点的值增加 \(x\),如果 \(v\) 的子孙 \(u\),与 \(v\) 的距离为 \(i\),那么需要给节点 \(u\) 上的值增加 \((x - i \times k)\)。两个点间的距离定义为两点之间的最短路。 - \(2\space v\) 输出节点 \(v\) 上的值。
对于每个询问,你需要输出 \(\mod (10^9+7)\) 后的值。
CF396C题解
将整个贡献拆开,变成 \(x + (dep_v-dep_u)\times k=x+dep_v\times k-dep_u\times k\)。
不难发现有三部分:\(x,\sum_{fa} dep_{fa}\times k_{fa},\sum_{fa} k_{fa}\),其中 \(fa\) 是 \(u\) 的祖先们
不难发现 \(x\) 可以区间覆盖解决,剩下两个树链剖分做一下就好了。
CF396C代码
#include<bits/stdc++.h> #define int long long using namespace std; inline int read(){ int x = 0, f= 1;char ch = getchar(); while(ch < '0' || ch > '9'){if(ch == '-') f = -1;ch = getchar();} while(ch >= '0' && ch <= '9'){x = (x << 1) + (x << 3) + (ch ^ 48);ch = getchar();} return x * f; } const int maxn = 3e5 + 10, mod = 1e9 + 7; int n, m; vector<int> edg[maxn]; int dep[maxn],siz[maxn], son[maxn], fa[maxn]; int dfn[maxn], idx, top[maxn]; int rev[maxn];//idx[dfn[u]] = u; void dfs1(int u,int f){ siz[u] = 1; for(int v : edg[u]){ if(v != f){ fa[v] = u; dep[v] = dep[u] + 1; dfs1(v, u); siz[u] += siz[v]; son[u] = siz[son[u]] > siz[v] ? son[u] : v; } } } void dfs2(int u,int t){ rev[dfn[u] = ++idx] = u;top[u] = t; if(!son[u])return; dfs2(son[u],t); for(int v : edg[u])if(v != fa[u] && v != son[u])dfs2(v, v); } struct Segment_Tree{ struct node{ int summ1, summ2, summ3; int tag, l, r;//tag for summ3 node(int s1 = 0,int s2 = 0,int s3 = 0,int l = 0,int r = 0,int tag = 0):summ1(s1),summ2(s2),summ3(s3),tag(tag),l(l),r(r){} }d[maxn << 2]; node mergenode(node l,node r){return node(l.summ1 + r.summ1,l.summ2 + r.summ2,l.summ3 + r.summ3,l.l,r.r);} void pushdown(int p){ if(d[p].tag != 0){ d[p << 1].tag += d[p].tag; d[p << 1].summ3 += (d[p << 1].r - d[p << 1].l + 1) * d[p].tag; d[p << 1 | 1].tag += d[p].tag; d[p << 1 | 1].summ3 += (d[p << 1 | 1].r - d[p << 1 | 1].l + 1) * d[p].tag; d[p << 1].tag %= mod;d[p << 1].summ3 %= mod;d[p << 1 | 1].tag %= mod;d[p << 1 | 1].summ3 %= mod; d[p].tag = 0; } } void build(int l,int r,int p){ if(l == r){d[p] = node(0,0,0,l,l);return;} int mid = l + r >> 1; build(l,mid,p << 1);build(mid + 1,r,p << 1 | 1); d[p] = mergenode(d[p << 1],d[p << 1 | 1]); } void update(int l,int r,int pos,int p,int upd){ if(l == r){ d[p].summ1 += upd;d[p].summ2 += upd * dep[rev[l]]; d[p].summ1 %= mod;d[p].summ2 %= mod; return; } int mid = l + r >> 1;pushdown(p); if(pos <= mid)update(l,mid,pos,p << 1,upd); else update(mid + 1,r,pos,p << 1 | 1,upd); d[p] = mergenode(d[p << 1],d[p << 1 | 1]); } void update(int l,int r,int s,int t,int p,int upd){ // printf("l = %lld, r = %lld, s = %lld, t = %lld\n",l,r,s,t); if(s <= l && r <= t){ d[p].summ3 += (r - l + 1) * upd;d[p].tag += upd; d[p].summ3 %= mod;d[p].tag %= mod; return; } int mid = l + r >> 1; pushdown(p); if(s <= mid)update(l,mid,s,t,p << 1,upd); if(mid < t)update(mid + 1,r,s,t,p << 1 | 1,upd); d[p] = mergenode(d[p << 1],d[p << 1 | 1]); } void update(int pos,int upd){update(1,n,pos,1,upd);} void update(int l,int r,int upd){update(1,n,l,r,1,upd);} node query(int l,int r,int s,int t,int p){ if(s <= l && r <= t)return d[p]; int mid = l + r >> 1;pushdown(p); if(t <= mid)return query(l,mid,s,t,p << 1); if(mid < s)return query(mid + 1,r,s,t,p << 1 | 1); return mergenode(query(l,mid,s,t,p << 1),query(mid + 1,r,s,t,p << 1 | 1)); } node query(int l,int r){return query(1,n,l,r,1);} }tree; signed main(){ n = read();int u , v, opt, w; for(int i = 2;i <= n;i++){ u = read();edg[i].push_back(u);edg[u].push_back(i); } dep[1] = 1; dfs1(1, 1);dfs2(1, 1);tree.build(1,n,1); // for(int i = 1;i <= n;i++){ // printf("%lld %lld %lld %lld %lld %lld %lld\n",i,dep[i],fa[i],son[i],siz[i],dfn[i],top[i]); // } m = read(); for(int i = 1;i <= m;i++){ opt = read();u = read(); if(opt == 1){ v = read(); w = read();tree.update(dfn[u],w); tree.update(dfn[u],dfn[u] + siz[u] - 1,v); } else{ int summ1 = 0, summ2 = 0;v = u; Segment_Tree::node tmp; int summ3 = tree.query(dfn[u],dfn[u]).summ3 % mod; while(u){ tmp = tree.query(dfn[top[u]],dfn[u]); summ1 += tmp.summ1;summ2 += tmp.summ2; u = fa[top[u]];summ1 %= mod;summ2 %= mod; } // printf("%lld %lld %lld\n",summ1,summ2,summ3); summ1 = (summ3 + summ2 - summ1 * dep[v]) % mod; while(summ1 < 0)summ1 += mod; printf("%lld\n",summ1); } } return 0; }
CF396D
CF396D题意
给你一个排列 \(p\),计算字典序不超过 \(p\) 的所有排列里,逆序对的总个数。
最后结果对 \(10^9+7\) 取模。
CF396D题解
考虑对每一位 \(i\) 计算,每次计算让 \([1,i-1]\) 位全都与原排列相等的、\([i,n]\) 位的排列小于原排列的排列逆序对总数。
设原序列为 \(p\),新序列为 \(q\)
不难发现这种计数方式覆盖所有新序列小于原序列的情况,剩下那个相等的情况自己算,别忘了加就好。
设 \(d_i=\sum_{j=i+1}^np_j<p_i\)
考虑如何计数,大致分三种:
- \([i+1,n]\) 之内的逆序对:不难发现,这个时候 \([i+1,n]\) 之内的数字具体是多少无所谓,所以可以利用离散化的思想,把这些数字变成 \([1,n-i-1]\) 的取值范围。然后这个答案就是 \({n-i\choose 2}^2\times (n-i - 2)!\times d_i\)。
- \(i\to[i+1,n]\) 的逆序对:不难发现,如果要求满足定义,需要 \(q_i=p_j,p_j<p_i,j\in[i+1,n]\)。然后发现,如果从最小的开始取值,逆序对就是 \(1,2,\dots,d_i\),然后后面随便排列,故答案是 \(\frac{d_i\times(d_i-1)}{2}\times(n-i)!\)。
- \([1,i-1]\) 内部和 \([1,i-1]\to [i,n]\):不难发现,对于每一位 \(1\le j<i\),后面一定有 \(d_j\) 个比他小的数字,无论后面怎么排的。然后 \(i\) 位置随便排列的方案是 \(d_i\),\([i+1,n]\) 全排列的方案是 \((n-i)!\),故答案是 \((n-i)!\times d_i\times \sum_{j=1}^{i-1}d_j\)。
没了。
CF396D代码
#include<bits/stdc++.h> #define int long long using namespace std; inline int read(){ int x = 0, f= 1;char ch = getchar(); while(ch < '0' || ch > '9'){if(ch == '-') f = -1;ch = getchar();} while(ch >= '0' && ch <= '9'){x = (x << 1) + (x << 3) + (ch ^ 48);ch = getchar();} return x * f; } const int maxn = 1e6 + 10, mod = 1e9 + 7; int n, p[maxn]; struct BIT{ int c[maxn << 2]; inline int lowbit(int x){return x & (-x);} void add(int x,int upd){for(;x <= n;x += lowbit(x))c[x] += upd;} int qry(int x){int ans = 0;for(;x;x -= lowbit(x))ans += c[x];return ans;} }tree; int d[maxn], sum[maxn]; int pw[maxn], invpw[maxn]; int qpow(int x,int a){ int res = 1; while(a){ if(a & 1)res = res * x % mod; x = x * x % mod;a >>= 1; } return res; } int C(int n,int m){return pw[n] * invpw[m] % mod * invpw[n - m] % mod;} signed main(){ n = read(); for(int i = 1;i <= n;i++){ p[i] = read(); d[i] = p[i] - tree.qry(p[i]) - 1; tree.add(p[i],1);sum[i] = (sum[i - 1] + d[i]) % mod; } int ans = sum[n]; pw[0] = 1; for(int i = 1;i <= 1e6;i++)pw[i] = pw[i - 1] * i % mod; invpw[(int)1e6] = qpow(pw[(int)1e6],mod - 2); for(int i = 1e6 - 1;i + 1;i--){invpw[i] = invpw[i + 1] * (i + 1) % mod;} for(int i = 1;i <= n;i++){ ans = (ans + d[i] * C(n - i,2) % mod * C(n - i,2) % mod * pw[n - i - 2] % mod) % mod; ans = (ans + d[i] * (d[i] - 1) / 2 % mod * pw[n - i] % mod) % mod; ans = (ans + d[i] * pw[n - i] % mod * sum[i - 1] % mod) % mod; } printf("%lld\n",ans); return 0; }
CF396E
不会QAQ
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 分享一个免费、快速、无限量使用的满血 DeepSeek R1 模型,支持深度思考和联网搜索!
· 使用C#创建一个MCP客户端
· 基于 Docker 搭建 FRP 内网穿透开源项目(很简单哒)
· ollama系列1:轻松3步本地部署deepseek,普通电脑可用
· 按钮权限的设计及实现