牛客 周赛84 20250312
牛客 周赛84 20250312
https://ac.nowcoder.com/acm/contest/103152
A:
题目大意:给定 \(3\) 个元素,定义陡峭值为两两相邻元素差的绝对值,判断给定的元素组陡峭值是否为 \(0\)
#include<bits/stdc++.h>
#define cintie ios::sync_with_stdio(false);cin.tie(0);cout.tie(0);
#define Trd int T;cin>>T;while (T--)solve();
#define LLinf 9e18;
#define Iinf 2e9
#define LL long long
#define Lc p<<1
#define Rc p<<1|1
#define lc(x) tr[x].ch[0]
#define rc(x) tr[x].ch[1]
using namespace std;
int main()
{
int a[3];
cin>>a[0]>>a[1]>>a[2];
sort(a,a+1);
if (a[0]==a[2]) cout<<"Yes";
else cout<<"No";
return 0;
}
当且仅当三个元素都相等时陡峭值为 \(0\)
B:
题目大意:给定 \(n\) 个元素的数组,求使这个数组陡峭值最小的排列方案数,并且输出这个最小值
#include<bits/stdc++.h>
#define cintie ios::sync_with_stdio(false);cin.tie(0);cout.tie(0);
#define Trd int T;cin>>T;while (T--)solve();
#define LLinf 9e18;
#define Iinf 2e9
#define LL long long
#define Lc p<<1
#define Rc p<<1|1
#define lc(x) tr[x].ch[0]
#define rc(x) tr[x].ch[1]
using namespace std;
int n;
int a[110];
int main()
{
cin>>n;
for (int i=1;i<=n;i++) cin>>a[i];
sort(a+1,a+n+1);
if (a[1]==a[n]){
cout<<1<<' '<<0;
return 0;
}
int ans=0;
for (int i=1;i<n;i++)
ans+=abs(a[i]-a[i+1]);
cout<<2<<' '<<ans;
return 0;
}
从小到大排序后,陡峭值最小,反过来从大到小排序也是一种方案
需要特判元素全部相同的情况,陡峭值为 \(0\),并且因为是排序,所以方案数只有一种
事实上排序后计算陡峭值可以 \(O(1)\) 得出,假设从小到大排序,那么 \(\lvert a_{i+1}-a_i\rvert=a_{i+1}-a_i\)
if (a[1]==a[n]) cout<<1<<' '<<0;
else cout<<2<<' '<<a[n]-a[1];
C:
题目大意:给定长 \(n\) 的字符串 \(a\) ,计算这个字符串所有长为 \(k\) 的连续子串的陡峭值之和
#include<bits/stdc++.h>
#define cintie ios::sync_with_stdio(false);cin.tie(0);cout.tie(0);
#define Trd int T;cin>>T;while (T--)solve();
#define LLinf 9e18;
#define Iinf 2e9
#define LL long long
#define Lc p<<1
#define Rc p<<1|1
#define lc(x) tr[x].ch[0]
#define rc(x) tr[x].ch[1]
using namespace std;
int n,k;
char a[1010];
int d[1010];
int p[1010];
int main()
{
cin>>n>>k;
for (int i=1;i<=n;i++) cin>>a[i];
for (int i=1;i<=n-1;i++) d[i]=abs(a[i+1]-a[i]);
for (int i=1;i<=n-1;i++) p[i]=p[i-1]+d[i];
int ans=0;
for (int i=k-1;i<=n;i++)
ans+=p[i]-p[i-k];
cout<<ans;
return 0;
}
前缀和搞定
需要计算的总和可以表示为
使用 \(d_i\) 表示 \(\lvert a_{i+1}-a_i\rvert\) ,计算相邻元素差的绝对值,\(p_i\) 表示前 \(i\) 个字符组成的子串的陡峭值
可以整理为
D:
题目大意:与 C 相同,增强了数据
#include<bits/stdc++.h>
#define cintie ios::sync_with_stdio(false);cin.tie(0);cout.tie(0);
#define Trd int T;cin>>T;while (T--)solve();
#define LLinf 9e18;
#define Iinf 2e9
#define LL long long
#define Lc p<<1
#define Rc p<<1|1
#define lc(x) tr[x].ch[0]
#define rc(x) tr[x].ch[1]
using namespace std;
int n,k;
char a[1000010];
int d[1000010];
int p[1000010];
int main()
{
cin>>n>>k;
for (int i=1;i<=n;i++) cin>>a[i];
for (int i=1;i<=n-1;i++) d[i]=abs(a[i+1]-a[i]);
for (int i=1;i<=n-1;i++) p[i]=p[i-1]+d[i];
LL ans=0;
for (int i=k-1;i<=n;i++)
ans+=1LL*p[i]-p[i-k];
cout<<ans;
return 0;
}
C题的前缀和同样能过,最优解
E:
题目大意:
#include<bits/stdc++.h>
#define cintie ios::sync_with_stdio(false);cin.tie(0);cout.tie(0);
#define Trd int T;cin>>T;while (T--)solve();
#define LLinf 9e18;
#define Iinf 2e9
#define LL long long
#define Lc p<<1
#define Rc p<<1|1
#define lc(x) tr[x].ch[0]
#define rc(x) tr[x].ch[1]
using namespace std;
struct edge{
int v,w;
};
struct E{
int u,v,w;
};
vector<E> EG;
vector<edge> e[100010];
int n;
LL d[100010];
void insert(int u,int v,int w){
e[u].push_back({v,w});
}
void dfs(int x,int y){
for (auto &[v,w]:e[x]){
if (v!=y){
dfs(v,x);
d[x]+=d[v]+w;
}
}
}
int main()
{
cin>>n;
for (int i=1;i<=n-1;i++){
int u,v;
cin>>u>>v;
int w=abs(u-v);
EG.push_back({u,v,w});
insert(u,v,w);
insert(v,u,w);
}
dfs(1,0);
LL sum=d[1];
LL ans=1e18;
for (auto &it:EG){
if (d[it.u]<d[it.v])
swap(it.u,it.v);
ans=min(ans,abs(sum-2*d[it.v]-it.w));
}
cout<<ans;
return 0;
}
DFS从任意节点出发,因为这是一棵树所以所有节点都能联通,那么将节点 \(1\) 作为根节点跑一边DFS(存无向边)
回溯时记录每个父节点的子树的陡峭值
枚举所有边,判断这个边被断开后,两个节点所在的子树的陡峭值之差
for (auto &it:EG){
if (d[it.u]<d[it.v])
swap(it.u,it.v);//保证v节点子树的陡峭值最小
ans=min(ans,abs(sum-2*d[it.v]-it.w));
}
保证 \(v\) 点的陡峭值最小,因为 \(u\) 节点可能是 \(v\) 的父亲
\(v\) 子树的陡峭值为 \(d_v\),断开后 \(u\) 节点子树的陡峭值可能不为 \(d_u\),需要通过总陡峭值代换计算
F:
题目大意:
#include<bits/stdc++.h>
#define cintie ios::sync_with_stdio(false);cin.tie(0);cout.tie(0);
#define Trd int T;cin>>T;while (T--)solve();
#define LLinf 9e18;
#define Iinf 2e9
#define LL long long
#define Lc p<<1
#define Rc p<<1|1
#define lc(x) tr[x].ch[0]
#define rc(x) tr[x].ch[1]
using namespace std;
const int mod=1e9+7;
int n;
int a[20];
LL quickpow(LL a,LL b,LL p){
LL res=1;
while (b){
if (b&1) res=res*a%p;
a=a*a%p;
b>>=1;
}
return res;
}
int main()
{
cin>>n;
for (int i=1;i<=n;i++) cin>>a[i];
sort(a+1,a+n+1);
LL sum=0;
for (int i=1;i<=n;i++){
for (int j=1;j<=n;j++){
sum+=abs(a[i]-a[j]);
}
}
cout<<sum*quickpow(n,mod-2,mod)%mod;//计算分母n在mod下的乘法逆元
return 0;
}
组合数学+逆元
F题的 \(n\le 18\) ,之间暴力计算每对相邻元素之差,最后累加即可
例如 \((a_i,a_j)\) 为一对,那么它们出现的次数为 \(A_{n-2}^{n-2}*(n-1)\) ,即考虑剩余元素的全排列,再插入这对元素的方案数
答案可以被表示为
G:
题目大意:相对F题扩大了数据
#include<bits/stdc++.h>
#define cintie ios::sync_with_stdio(false);cin.tie(0);cout.tie(0);
#define Trd int T;cin>>T;while (T--)solve();
#define LLinf 9e18;
#define Iinf 2e9
#define LL long long
#define Lc p<<1
#define Rc p<<1|1
#define lc(x) tr[x].ch[0]
#define rc(x) tr[x].ch[1]
using namespace std;
const LL mod=1e9+7;
int n;
LL a[200010];
LL p[200010];
LL quickpow(LL a,LL b,LL p){
LL res=1;
while (b){
if (b&1) res=res*a%p;
a=a*a%p;
b>>=1;
}
return res;
}
int main()
{
cin>>n;
for (int i=1;i<=n;i++) cin>>a[i];
sort(a+1,a+n+1);
for (int i=1;i<=n;i++) p[i]=(p[i-1]+a[i])%mod;
LL sum=0;
for (int i=1;i<=n;i++)
sum+=1ll*(a[i]*(i-1)%mod-p[i-1]+mod)%mod;//确保为正数
cout<<2*sum%mod * quickpow(n,mod-2,mod)%mod; //随时取模
return 0;
}
给定的 \(n\le 2\times10^5\) ,两层暴力算 \({\rm{sumof}}\ d\) 一定爆炸,考虑优化计算 \(\sum_{i=1}^{n} \sum_{j=1}^{n} d_{i,j}\) 的步骤
因为从小到大排序后的 \(a\) 满足 \(a_{i+1}-a_i\ge 0\) ,并且 \(d_{i,j}=d_{j,i}\) 所以只需要算一半即可
计算所有的 \((a_i,a_j)\) 可以被简化为
其中 \(\sum_{j=1}^{i-1}\) 可以利用前缀和 \(O(1)\) 得到,所以总计算的时间复杂度为 \(O(n)\),总时间复杂度为 \(O(n\log n)\)