CF396

CF396

Codeforces Round 232 (Div. 1)

CF396A

link

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

link

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

link

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

link

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

posted @   Call_me_Eric  阅读(12)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 分享一个免费、快速、无限量使用的满血 DeepSeek R1 模型,支持深度思考和联网搜索!
· 使用C#创建一个MCP客户端
· 基于 Docker 搭建 FRP 内网穿透开源项目(很简单哒)
· ollama系列1:轻松3步本地部署deepseek,普通电脑可用
· 按钮权限的设计及实现
Live2D
欢迎阅读『CF396』
点击右上角即可分享
微信分享提示