A
均匀分组是最优的
n
−
⌈
n
m
⌉
>
k
n-\lceil {n \over m} \rceil>k
n−⌈mn⌉>k输出
Y
e
s
Yes
Yes
// LUOGU_RID: 155897482
#include <bits/stdc++.h>
#define int long long
#define ull unsigned long long
using namespace std;
void paralysis(){
int n,m,k;
cin>>n>>m>>k;
if (n-ceil(1.0*n/m)<=k){
cout<<"NO\n";
}else{
cout<<"YES\n";
}
}
signed main(){
ios::sync_with_stdio(false);
cin.tie(nullptr);
cout.tie(nullptr);
int T=1;
cin>>T;
while (T--){
paralysis();
}
return 0;
}
B
a
1
a_1
a1即为美丽数组最后的数,只要删去
a
a
a中最小的
a
1
a_1
a1连通块
// LUOGU_RID: 155897774
#include <bits/stdc++.h>
#define int long long
#define ull unsigned long long
using namespace std;
void paralysis(){
int n;
cin>>n;
vector<int> a(n+1);
for (int i=1;i<=n;i++){
cin>>a[i];
}
int sum=0,ans=1e9,flag=0;
for (int i=1;i<=n;i++){
if (a[i]==a[1]){
sum++;
}else{
flag=1;
ans=min(ans,sum);
sum=0;
}
}
if (sum&&flag){
ans=min(ans,sum);
}
cout<<(ans==1e9?-1:ans)<<"\n";
}
signed main(){
ios::sync_with_stdio(false);
cin.tie(nullptr);
cout.tie(nullptr);
int T=1;
cin>>T;
while (T--){
paralysis();
}
return 0;
}
C
分两个序列
发现
s
1
+
s
2
s1+s2
s1+s2操作后始终为定值,两者差越小乘积越大,所以贪心:
前缀相等,大数放
s
1
s1
s1,小数放
s
2
s2
s2
前缀不相等,反过来
// LUOGU_RID: 155977710
#include <bits/stdc++.h>
#define int long long
#define ull unsigned long long
using namespace std;
void paralysis(){
string s1,s2;
cin>>s1>>s2;
bool flag=0;
for (int i=0;i<s1.size();i++){
if (!flag){
if (s2[i]>s1[i]){
swap(s1[i],s2[i]);
flag=1;
}else if (s1[i]>s2[i]){
flag=1;
}
}else{
if (s1[i]>s2[i]){
swap(s1[i],s2[i]);
}
}
}
cout<<s1<<"\n"<<s2<<"\n";
}
signed main(){
ios::sync_with_stdio(false);
cin.tie(nullptr);
cout.tie(nullptr);
int T=1;
cin>>T;
while (T--){
paralysis();
}
return 0;
}
D
首先对于一个序列
a
a
a ,最小分组是一个经典问题
对于
m
a
x
(
a
)
>
=
s
u
m
(
a
)
−
m
a
x
(
a
)
max(a)>=sum(a)-max(a)
max(a)>=sum(a)−max(a),分
m
a
x
(
a
)
max(a)
max(a)组
否则分
⌈
s
u
m
(
a
)
2
⌉
\lceil {sum(a) \over 2} \rceil
⌈2sum(a)⌉ 组
再回到原问题, a i a_i ai 选与不选是一个背包问题,再由题目所说球总数不超过5000,所以 f i , j f_{i,j} fi,j统计个数,那么每次答案只会在选 a i a_i ai时产生,发现 s u m sum sum 在 j j j 中表示了, m a x max max 可以对 a a a 排序后再dp快速查询
// LUOGU_RID: 155902288
#include <bits/stdc++.h>
#define ll long long
constexpr int mod=998244353;
using namespace std;
void paralysis(){
int n;
cin>>n;
vector<int> a(n+1);
int sum=0;
for (int i=1;i<=n;i++){
cin>>a[i];
sum+=a[i];
}
sort(a.begin()+1,a.end());
vector<vector<int>> f(n+1,vector<int>(sum+1));
f[0][0]=1;
ll ans=0;
for (int i=1;i<=n;i++){
for (int j=a[i];j<=sum;j++){
f[i][j]=f[i-1][j-a[i]];
if (a[i]>j-a[i]){
ans=(ans+1LL*f[i][j]*a[i]%mod)%mod;
}else{
ans=(ans+1LL*f[i][j]*((j+1)/2)%mod)%mod;
}
}
for (int j=0;j<=sum;j++){
f[i][j]=(f[i][j]+f[i-1][j])%mod;
}
}
cout<<ans<<"\n";
}
int main(){
ios::sync_with_stdio(false);
cin.tie(nullptr);
cout.tie(nullptr);
int T=1;
// cin>>T;
while (T--){
paralysis();
}
return 0;
}
E
首先对于
k
=
1
k=1
k=1 时,它是一个经典问题 [NOIP2018 提高组] 铺设道路, 遍历
a
i
a_i
ai,当
a
i
>
a
i
−
1
a_i>a_{i-1}
ai>ai−1时才产生贡献。然后就很好求了
对于 k > 1 k>1 k>1的情况,处理一下 a i = ⌈ a i k ⌉ a_i={\lceil {a_i \over k} \rceil} ai=⌈kai⌉,转换为经典问题,暴力做是 O ( n a ) O(na) O(na)的
方法一:
对于除法操作,我们想是否可以数论分块,优先枚举
i
i
i ,然后枚举
k
k
k ,转换一下向上取整
⌈
a
i
k
⌉
=
⌊
a
i
+
k
−
1
k
⌋
{\lceil {a_i \over k} \rceil}={\lfloor {{a_i+k-1} \over k} \rfloor}
⌈kai⌉=⌊kai+k−1⌋ ,然后就可以了
// LUOGU_RID: 155941020
#include <bits/stdc++.h>
#define int long long
using namespace std;
void paralysis(){
int n;
cin>>n;
vector<int> a(n+1);
int maxn=0;
for (int i=1;i<=n;i++){
cin>>a[i];
maxn=max(maxn,a[i]);
}
vector<int> ans(maxn+2);
for (int i=1;i<n;i++){
int use1=a[i]-1,use2=a[i+1]-1;
for (int l=1,r=1;l<=maxn;l=r+1){
r=min(use1/l?use1/(use1/l):maxn,use2/l?use2/(use2/l):maxn);
int x=use1/l+1,y=use2/l+1;
if (y>x){
ans[l]+=y-x;
ans[r+1]-=y-x;
}
}
}
for (int i=1;i<=maxn;i++){
ans[i]+=ans[i-1];
cout<<ans[i]+(a[1]+i-1)/i<<" ";
}
cout<<"\n";
}
signed main(){
ios::sync_with_stdio(false);
cin.tie(nullptr);
cout.tie(nullptr);
int T=1;
// cin>>T;
while (T--){
paralysis();
}
return 0;
}
方法二:
发现本质还是一个求贡献的式子
∑
a
i
>
a
i
−
1
⌈
a
i
k
⌉
−
⌈
a
i
−
1
k
⌉
\sum_{a_i>a_{i-1}} {\lceil{a_i \over k} \rceil} -{\lceil{a_{i-1} \over k} \rceil}
∑ai>ai−1⌈kai⌉−⌈kai−1⌉
对于枚举
k
k
k 后,其实可以看作枚举了因数,我们可以类似用埃氏筛的方法,再枚举
⌈
a
i
k
⌉
{\lceil{a_i \over k} \rceil}
⌈kai⌉,这样计算出
a
i
a_i
ai的范围,前缀和预处理一下
a
i
a_i
ai 的系数即可,因为是调和级数,
O
(
a
l
o
g
a
)
O(aloga)
O(aloga)
#include <bits/stdc++.h>
#define int long long
using namespace std;
void paralysis(){
int n;
cin>>n;
vector<int> a(n+1);
int maxn=0;
for (int i=1;i<=n;i++){
cin>>a[i];
maxn=max(maxn,a[i]);
}
vector<int> sum(maxn+1);
sum[a[1]]++;
for (int i=1;i<n;i++){
if (a[i+1]>a[i]){
sum[a[i+1]]++;
sum[a[i]]--;
}
}
for (int i=1;i<=maxn;i++){
sum[i]+=sum[i-1];
}
for (int k=1;k<=maxn;k++){
int ans=0;
for (int j=1;j<=(maxn+k-1)/k;j++){
ans+=j*(sum[min(maxn,j*k)]-sum[(j-1)*k]);
}
cout<<ans<<" \n"[k==maxn];
}
}
signed main(){
ios::sync_with_stdio(false);
cin.tie(nullptr);
cout.tie(nullptr);
int T=1;
// cin>>T;
while (T--){
paralysis();
}
return 0;
}