概述
也许更好的阅读体验
本人只做出了前四题,所以只写前四题的题解
A 数 数 mathcal{A 数数} A 数数
D e s c r i p t i o n mathcal{Description} Description
给出
n
n
n,求
∑
i
=
1
n
∑
j
=
1
n
i
×
j
begin{aligned}sum_{i=1}^nsum_{j=1}^nitimes jend{aligned}
i=1∑nj=1∑ni×j 和
∏
i
=
1
n
∏
j
=
1
n
i
×
j
begin{aligned}prod_{i=1}^nprod_{j=1}^nitimes jend{aligned}
i=1∏nj=1∏ni×j
答案对
998244353
998244353
998244353取模
S o l u t i o n mathcal{Solution} Solution
这是一道送分题
∑
i
=
1
n
∑
j
=
1
n
i
×
j
=
∑
i
=
1
n
i
×
(
∑
j
=
1
n
j
)
=
(
∑
j
=
1
n
j
)
×
(
∑
i
=
1
n
i
)
=
(
n
(
n
+
1
)
2
)
2
begin{aligned}sum_{i=1}^nsum_{j=1}^nitimes j=sum_{i=1}^nitimesleft(sum_{j=1}^njright)=left(sum_{j=1}^njright)timesleft(sum_{i=1}^niright)=left(frac{n(n+1)}{2}right)^2end{aligned}
i=1∑nj=1∑ni×j=i=1∑ni×(j=1∑nj)=(j=1∑nj)×(i=1∑ni)=(2n(n+1))2
这个做多了题目就是一眼的事了
它的几何性质是一个
n
×
n
ntimes n
n×n的矩阵其所有子矩阵个数
∏ i = 1 n ∏ j = 1 n i × j = ∏ i = 1 n i n ∏ j = 1 n j = ( ∏ j = 1 n j ) n ∏ i = 1 n i n = ( ∏ j = 1 n j n ) ∏ i = 1 n i n = ∏ i = 1 n i 2 n = ( ∏ i = 1 n i ) 2 n begin{aligned}prod_{i=1}^nprod_{j=1}^nitimes j=prod_{i=1}^ni^nprod_{j=1}^nj = left(prod_{j=1}^njright)^nprod_{i=1}^ni^n = left(prod_{j=1}^nj^nright)prod_{i=1}^ni^n = prod_{i=1}^ni^{2n}=left(prod_{i=1}^niright)^{2n}end{aligned} i=1∏nj=1∏ni×j=i=1∏ninj=1∏nj=(j=1∏nj)ni=1∏nin=(j=1∏njn)i=1∏nin=i=1∏ni2n=(i=1∏ni)2n
不会推式子怎么办!
和博主一样直接看吧!
第一个就不说了
第二个反正都是乘,我们直接考虑有多少个被乘了不就可以了吗
考虑
i
i
i被乘的次数,在形如
i
×
j
itimes j
i×j这样的情况每个都有
i
i
i,共有
n
+
1
n+1
n+1个
i
i
i (
i
×
i
itimes i
i×i这样的情况有两个
i
i
i)
形如
j
×
i
(
j
!
=
i
)
j times i(j!=i)
j×i(j!=i)的情况对于每个
j
j
j都会有,共有
n
−
1
n-1
n−1个,所以
i
i
i会被乘
2
n
2n
2n次
C o d e mathcal{Code} Code
/*******************************
Author:Morning_Glory
LANG:C++
Created Time:2019年09月14日 星期六 19时06分48秒
*******************************/
#include <cstdio>
#include <fstream>
#define ll long long
using namespace std;
const int maxn = 10000005;
const int mod = 998244353;
//{{{cin
struct IO{
template<typename T>
IO & operator>>(T&res){
res=0;
bool flag=false;
char ch;
while((ch=getchar())>'9'||ch<'0') flag|=ch=='-';
while(ch>='0'&&ch<='9') res=(res<<1)+(res<<3)+(ch^'0'),ch=getchar();
if (flag) res=~res+1;
return *this;
}
}cin;
//}}}
int T,n,ans1,ans2;
int s[maxn],mi[maxn];
//{{{ksm
int ksm (int a,int b)
{
int s=1;
for (;b;b>>=1,a=1ll*a*a%mod)
if (b&1) s=1ll*s*a%mod;
return s;
}
//}}}
int main()
{
s[1]=1;
for (int i=2;i<=maxn-5;++i) s[i]=1ll*s[i-1]*i%mod;
cin>>T;
while (T--){
cin>>n;
ans1=1ll*n*(n+1)/2%mod;
ans1=1ll*ans1*ans1%mod;
ans2=ksm(s[n],2*n)%mod;
printf("%d %dn",ans1,ans2);
}
return 0;
}
B G a l a h a d mathcal{B Galahad} B Galahad
D e s c r i p t i o n mathcal{Description} Description
魔女要测试骑士的能力,要求他维护一个长度为 n n n的序列,每次要询问一个区间的和。
但是魔女觉得太简单了,骑士能轻松记住 n n n个数的前缀和。
于是,魔女要求他回答一个区间的和,但如果某一个数在这个区间出现了多次,这个数只能被计算一次。
n , m ≤ 500000 , 1 ≤ a i ≤ 500000 n,mleq 500000,1leq a_ileq 500000 n,m≤500000,1≤ai≤500000
S o l u t i o n mathcal{Solution} Solution
这题好像是曾经的莫队板子题,但是开大了数据范围,于是咱的莫队就
T
T
T了
换个思路
先将查询按照
r
r
r从小到大排序
令
l
a
s
t
i
last_i
lasti表示数字
i
i
i上一次的出现位置
当我们找到一个数
i
i
i时,只要在当前位置加上
i
i
i,在
l
a
s
t
i
last_i
lasti减去
i
i
i即可保证不会重复计算
由于我们将查询按照
r
r
r从小到大排序了
所以每个当前查询一定经过现在这个位置
而
l
≤
l
a
s
t
i
lleq last_i
l≤lasti的情况,我们已经将
l
a
s
t
i
last_i
lasti的答案抵消了,所以每个
i
i
i只会被计算一次
而现在实际上就是要求一段区间的加和,这个用树状数组维护就可以了
C o d e mathcal{Code} Code
/*******************************
Author:Morning_Glory
LANG:C++
Created Time:2019年09月14日 星期六 19时18分27秒
*******************************/
#include <cstdio>
#include <algorithm>
#define ll long long
using namespace std;
const int maxn = 1000006;
//{{{cin
struct IO{
template<typename T>
IO & operator>>(T&res){
res=0;
bool flag=false;
char ch;
while((ch=getchar())>'9'||ch<'0') flag|=ch=='-';
while(ch>='0'&&ch<='9') res=(res<<1)+(res<<3)+(ch^'0'),ch=getchar();
if (flag) res=~res+1;
return *this;
}
}cin;
//}}}
struct Q{
int l,r,id;
}q[maxn];
int n,m;
int a[maxn],last[maxn];
ll c[maxn],ans[maxn];
bool cmp(Q a,Q b){ return a.r<b.r;}
//{{{query
ll query(int x)
{
ll res=0;
while(x>0){ res+=c[x];x-=x&-x;}
return res;
}
//}}}
//{{{insert
void insert(int x,int v)
{
while(x<=n){ c[x]+=v;x+=x&-x;}
}
//}}}
int main()
{
cin>>n>>m;
for(int i=1;i<=n;i++) cin>>a[i];
for(int i=1;i<=m;i++) cin>>q[i].l>>q[i].r,q[i].id=i;
sort(q+1,q+m+1,cmp);
int t=1;
for(int i=1;i<=n;i++){
if(last[a[i]]) insert(last[a[i]],-a[i]);
insert(i,a[i]);
while(i==q[t].r) ans[q[t].id]=query(q[t].r)-query(q[t].l-1),t++;
last[a[i]]=i;
}
for(int i=1;i<=m;i++) printf("%lldn",ans[i]);
return 0;
}
C 烹 饪 mathcal{C 烹饪} C 烹饪
D e s c r i p t i o n mathcal{Description} Description
小
Y
Y
Y上山拜师学艺,经过
10
10
10年之长的厨艺练习,已成为当世名厨,今天他接受邀请,在众人面前展示自己高超的厨艺。
人们给小
Y
Y
Y提供了
n
n
n种食物,每种食物无限量供应,每种食物都有一个美味值,记为
a
i
a_i
ai。
小 Y Y Y为了展示他的厨艺,他需要挑选出食材,使自己可以烹饪出任意正整数美味值的菜肴,初始时菜肴的美味值为 0 0 0,每次加入一种食材,他可以选择让菜肴的美味值上升 a i a_i ai,也可以选择让菜肴的美味值下降 a i a_i ai(或许最后会弄出来黑暗料理?)。
作为当世名厨,小
Y
Y
Y自然知道该怎么挑选食材最佳。可是他并不知道有多少种最佳的挑选食材方案,于是他找到了你来帮忙。
我们使用无序数列
(
b
1
,
b
2
,
…
,
b
m
)
left(b_1,b_2,ldots,b_mright)
(b1,b2,…,bm)来表示从原来的
n
n
n种食材中挑选出了
m
m
m种食材,第
i
i
i种食材编号为
b
i
b_i
bi的方案。同时你需要注意,
(
1
,
2
)
left(1,2right)
(1,2)和
(
2
,
1
)
left(2,1right)
(2,1)为同一种方案,且当
i
!
=
j
i!=j
i!=j时
b
i
!
=
b
j
b_i != b_j
bi!=bj。
最佳的挑选食材方案指,挑选出 种食材
m
m
m种食材
(
1
≤
m
≤
n
)
(1leq mleq n)
(1≤m≤n),让他们能够组合出任意正整数美味值的菜肴。
答案对 998244353 998244353 998244353取模。
S o l u t i o n mathcal{Solution} Solution
又是一个计数题
计数题有很多方法,
d
p
dp
dp,容斥,递推等都可以计数
最开始我打算按照一定方式不重不漏的组合开始计算
但是想了半天除了几个错误的方法仍然没什么思路
在想的时候发现按照一定方式不如直接算不合法的
于是考虑容斥
首先我们要知道一点
能组合出任意正整数
⇔
Leftrightarrow
⇔能组合出
1
1
1
所以我们将目标锁定在能否组合出
1
1
1
合法的方法=所有方法-不能组合出
1
1
1的方法
考虑哪些一定能组合出
1
1
1
最显然的是
(
a
,
a
+
1
)
(a,a+1)
(a,a+1)这样的是合法的
而如果
a
a
a是个合数,设
k
k
k为
a
a
a的一个因子
那么
(
k
,
a
+
1
)
(k,a+1)
(k,a+1)也是合法的
可以想到任意
a
,
b
a,b
a,b只要满足
a
x
−
b
y
=
1
ax-by=1
ax−by=1就合法
只要这个式子有解就合法
a
x
+
(
−
b
y
)
=
1
ax+(-by)=1
ax+(−by)=1
而其有解条件是
g
c
d
(
a
,
b
)
=
1
gcd(a,b)=1
gcd(a,b)=1
其实换个思路也可以得到
只要是互质的就可以满足条件,因为它们之间一个对另一个取模的剩余系是可以得到 1 1 1的
或者考虑只要是不互质的数就不可以满足条件,因为无论他们怎么加减,结果都会有他们的最大公约数在里面
于是可以知道,得要弄出这样的序列
a
1
,
a
2
,
…
,
a
n
a_1,a_2,ldots,a_n
a1,a2,…,an,其满足
g
c
d
(
a
1
,
a
2
,
…
,
a
n
)
=
1
gcd(a_1,a_2,ldots,a_n)=1
gcd(a1,a2,…,an)=1
求这样的数列个数就可以考虑容斥了
g
c
d
=
1
gcd=1
gcd=1的序列数
=
=
=总序列数
−
g
c
d
!
=
1
-gcd !=1
−gcd !=1的序列数
考虑枚举它们的
g
c
d
gcd
gcd,令
f
[
i
]
f[i]
f[i]表示以
i
i
i作为
g
c
d
gcd
gcd的序列数,那么答案就是
f
[
1
]
f[1]
f[1]
而
f
[
i
]
=
g
c
d
f[i]=gcd
f[i]=gcd是
i
i
i的倍数的序列数
−
f
[
i
∗
2
]
−
f
[
i
∗
3
]
−
…
−
f
[
i
∗
k
]
-f[i*2]-f[i*3]-ldots-f[i*k]
−f[i∗2]−f[i∗3]−…−f[i∗k]
用
n
u
m
[
i
]
num[i]
num[i]表示有多少个数是
i
i
i的倍数
g
c
d
gcd
gcd是
i
i
i的倍数的序列数=
2
n
u
m
[
i
]
−
1
2^{num[i]}-1
2num[i]−1
2 n u m [ i ] − 1 2^{num[i]}-1 2num[i]−1就是有 n u m [ i ] num[i] num[i]个数可选可不选,但必须至少选一个的方案数
这样这道题就得到解决了
C o d e mathcal{Code} Code
/*******************************
Author:Morning_Glory
LANG:C++
Created Time:2019年09月14日 星期六 19时33分10秒
*******************************/
#include <cstdio>
#include <fstream>
#include <algorithm>
using namespace std;
const int maxn = 3005;
const int mx = 2000;
const int mod = 998244353;
//{{{cin
struct IO{
template<typename T>
IO & operator>>(T&res){
res=0;
bool flag=false;
char ch;
while((ch=getchar())>'9'||ch<'0') flag|=ch=='-';
while(ch>='0'&&ch<='9') res=(res<<1)+(res<<3)+(ch^'0'),ch=getchar();
if (flag) res=~res+1;
return *this;
}
}cin;
//}}}
int n;
int a[maxn],num[maxn],f[maxn],s[maxn],mi[maxn];
void add (int &x,int y){ x=((x+y)%mod+mod)%mod;}
//{{{gcd
int gcd (int a,int b)
{
if (!b) return a;
return gcd(b,a%b);
}
//}}}
int main()
{
mi[0]=1;
cin>>n;
for (int i=1;i<=n;++i) cin>>a[i];
for (int i=1;i<=3000;++i) mi[i]=2ll*mi[i-1]%mod;
for (int i=mx;i>=1;--i){
for (int j=1;j<=n;++j)
if (a[j]%i==0) ++num[i];
f[i]=(mi[num[i]]+mod-1)%mod;
for (int j=2;i*j<=mx;++j) add(f[i],-f[i*j]);
}
printf("%dn",f[1]);
return 0;
}
D 粉 丝 群 mathcal{D 粉丝群} D 粉丝群
D e s c r i p t i o n mathcal{Description} Description
在 w j y y y wjyyy wjyyy粉丝群中,除了 w j y wjy wjy一共有 n n n个人,编号为 1 1 1到 n n n。
大家准备一起膜爆 w j y wjy wjy,也就是说复读 2 n 2n 2n次消息,并且由于这 n n n个人在膜 w j y wjy wjy上是统一的,所以每个人都必须至少复读一次。
w j y wjy wjy想要禁言掉一些人,但是这里是 w j y wjy wjy粉丝群,不能随便禁言膜 w j y wjy wjy的人,于是 w j y wjy wjy定下一个规则:
如果这 n n n个人,能够分成两组,使得两个组中所有人的复读次数的加和是相同的,那么这 n n n个人都要被禁言。
这 n n n个人开始讨论,他们不想被暴政。
那么问题来了,有多少种复读的分配方法,使得 w j y y y wjyyy wjyyy没法把这 n n n个人分成满足以上条件的两组?
然而 w j y wjy wjy的粉丝太多了,您只要输出分配方法的种数以及这些分配方法中字典序第 k k k小的分配方式的异或和即可。
注意:如果有两个人,则复读次数分别为 ( 1 , 3 ) (1,3) (1,3)与复读次数分别为 ( 3 , 1 ) (3,1) (3,1) 算两种不同的分配方式。
S o l u t i o n mathcal{Solution} Solution
这题打表找规律也可以看出来的
首先每人都至少复读一次,就先记每人都复读了一次
1 1 1 | 2 2 2 | 3 3 3 | … ldots … | n n n |
---|---|---|---|---|
1 1 1 | 1 1 1 | 1 1 1 | … ldots … | 1 1 1 |
现在还剩 n n n次复读要分给这些人,并且要使它们无法分成两组复读次数加和是一样的
先考虑极端情况
若把这
n
n
n次机会再每人一次
那么最终就是每人复读两次
当
n
n
n为奇数时,这样是满足条件的,而
n
n
n为偶数是不满足的
若把这
n
n
n次机会全部给一个人,那么那个人就有
n
+
1
n+1
n+1次复读
剩下的人加起来也不会超过他的复读次数,所以全部给一个人也是满足条件的
再考虑普遍的情况
若有些人没有被分到复读次数(即只复读了一次)
我们把复读次数大于
1
1
1的人尽量平均分成两组,两组复读次数分别为
x
,
y
(
x
<
y
)
x,y(x<y)
x,y(x<y)
那么一定有
y
−
x
y-x
y−x个人是只复读了一次的
这里可以自己手玩
3
,
4
,
5
,
6
3,4,5,6
3,4,5,6这样的看一看以便理解
把这些人放到
x
x
x那边去,就刚好两组平均了
所以这些都不满足条件
综上
只有把
n
n
n次复读全部给一个人是绝对满足的
当
n
n
n为奇数时所有人都复读两次也是可以的
而字典序肯定是
(
1
,
1
,
…
1
,
n
+
1
)
,
(
1
,
1
,
…
,
n
+
1
,
1
)
…
(
n
+
1
,
1
,
…
,
1
,
1
)
(1,1,ldots1,n+1),(1,1,ldots,n+1,1)ldots(n+1,1,ldots,1,1)
(1,1,…1,n+1),(1,1,…,n+1,1)…(n+1,1,…,1,1)这样子的
共有
n
n
n种满足条件,当
n
n
n为奇数时还要多一种
(
2
,
2
,
…
,
2
,
2
)
(2,2,ldots,2,2)
(2,2,…,2,2)
前面一堆
1
1
1异或起来不是
1
1
1就是
0
0
0都是可以
O
(
1
)
O(1)
O(1)判的
C o d e mathcal{Code} Code
/*******************************
Author:Morning_Glory
LANG:C++
Created Time:2019年09月14日 星期六 20时36分37秒
*******************************/
#include <cstdio>
#include <fstream>
#define ll long long
using namespace std;
//{{{cin
struct IO{
template<typename T>
IO & operator>>(T&res){
res=0;
bool flag=false;
char ch;
while((ch=getchar())>'9'||ch<'0') flag|=ch=='-';
while(ch>='0'&&ch<='9') res=(res<<1)+(res<<3)+(ch^'0'),ch=getchar();
if (flag) res=~res+1;
return *this;
}
}cin;
//}}}
ll n,k;
int main()
{
cin>>n>>k;
if (n==1){ printf("1n2n");return 0;}
if (n&1) printf("%lldn%lldn",n+1,(k==n)?2:n+1);
else printf("%lldn%lldn",n,(n+1)^1);
return 0;
}
如有哪里讲得不是很明白或是有错误,欢迎指正
如您喜欢的话不妨点个赞收藏一下吧
最后
以上就是开放学姐为你收集整理的牛客练习赛52 题解的全部内容,希望文章能够帮你解决牛客练习赛52 题解所遇到的程序开发问题。
如果觉得靠谱客网站的内容还不错,欢迎将靠谱客网站推荐给程序员好友。
发表评论 取消回复