好久没写题目了,今天趁着校赛选拔把不会的都写一写。
这场ak的人是我见过最多的了,快500人了都(悲),少写一点就500开外了,真是上分(掉分)的好机会啊。
A.小红的陡峭值(一)
陡峭值:数组里所有相邻两个数绝对值之和
判断给出的三个数的陡峭值是否为0,直接模拟就好了
using LL = long long;
void solve(){
int a1,a2,a3;
cin>>a1>>a2>>a3;
if(abs(a1-a2)+abs(a2-a3)==0){
cout<<"Yes";
}
else{
cout<<"No";
}
}
B.小红的陡峭值(二)
给出一个数组,可以将其任意重新排列,能否使得数组的陡峭值尽可能小,输出最小值和对应的方案数。
其实简单分析后可以得知,将其sort从小到大排序后,此时陡峭值之和一定是最小值,倒序也是一样,固有2种方法,再求一下最小值即可。(注意当元素只有一种时只有一种排列方式,这个case应该挡了不少人)
void solve(){
int n;
cin>>n;
vector<int> a(n+1,0);
int cnt = 0;
map<int,int> q;
for(int i=1;i<=n;i++){
cin>>a[i];
if(!q[a[i]]){
cnt++;
q[a[i]]++;
}
}
LL ans = 0;
sort(a.begin(),a.end());
for(int i=1;i<n;i++){
ans+=abs(a[i]-a[i+1]);
}
if(cnt==1){//如果此时只有一种元素
cout<<1<<" "<<ans<<endl;
}
else{
cout<<2<<" "<<ans<<endl;
}
}
C.小红的陡峭值(三)(easy)
这里定义陡峭值为相邻了两个字符Ascii码的绝对值之和,其实没差。
要求出长度为n的字符串中所有长度为k的连续子串的值之和。
这里的n的范围到1e3,很小,可以考虑直接暴力,但是后面一题的范围就暴力不了了。
我们先考虑暴力做法,先遍历每个字符,然后寻找往后k-1个的连续字串,分离出来,再累加求陡峭值,时间复杂度就是o(n²)了。
using LL = long long;
void solve(){
int n,k;
cin>>n>>k;
string s;
cin>>s;
LL ans = 0;
for(int i=0;i<n-k+1;i++){
string t;
for(int j=i;j<=i+k-1;j++){
t += s[j];
}
for(int j=0;j<t.length()-1;j++){
ans+=abs(t[j]-t[j+1]);
}
}
cout<<ans<<endl;
}
D.小红的陡峭值(三)(hard)
本题与第三题的区别就是数据量大,无法使用双循环暴力枚举,我们可以考虑贡献法求解。
我们可以先模拟一下,,比如当 abcdef k=3 的时候,会有
abcdef
123456
abc
bcd
cde
def
不难发现,每个字符出现的次数是有规律的,我们可以采用差分来计算字符出现的次数
for(int i=0;i<n-k+1;i++){
a[i]++;
a[i+k]--;
}
123321 这个就是结果,然后我们还要计算两两之间的陡峭值,可以发现,就是两个字符成对的个数,比如bc 自然在 abc 和bcd里面出现,所以出现次数一定是差分结果中间的小值,而且不会超过k-1,这样我们就得到了核心代码。
using LL = long long;
void solve(){
int n,k;
cin>>n>>k;
string s;
cin>>s;
LL ans = 0;
vector<int> a(n+1,0);
for(int i=0;i<n-k+1;i++){
a[i]++;
a[i+k]--;
}
for(int i=1;i<n;i++){
a[i]+=a[i-1];
}
for(int i=0;i<n-1;i++){
ans += abs(s[i]-s[i+1])*min(k-1,min(a[i],a[i+1]));//最后还要乘以字符之间的差值
}
cout<<ans<<endl;
}
E.小红的陡峭值(四)
给定一棵树,树的陡峭值为连接节点的绝对值之和,要求断掉一条边,求形成两棵树陡峭值之差的最小值。
可以先跑一遍dfs求出整棵树的值,即为1号节点,然后计算每个节点的子数的值,分别存入数组。
再跑一遍dfs,对所有的边进行计算。
设(u,v) f[v] 为以v为根节点的子树值,ans为f[1]
一棵树 f[v]
另一棵树 ans-abs(u-v)-f[v]
枚举取最小值即可
#include <bits/stdc++.h>
using namespace std;
using LL = long long;
vector<int> g[202020];
int n;
LL ans = 1e18;
vector<LL> s(202020,0);
void dfs(int u,int fa){
for(int i=0;i<g[u].size();i++){
int t = g[u][i];
if(t==fa) continue;
dfs(t,u);
s[u] += s[t] + abs(u-t);
}
}
void dfs2(int u,int fa){
for(int i=0;i<g[u].size();i++){
int t = g[u][i];
if(t==fa) continue;
dfs2(t,u);
ans = min(ans,abs((s[1]-s[t]-abs(u-t))-s[t]));
}
}
void solve(){
cin>>n;
for(int i=1;i<n;i++){
int u,v;
cin>>u>>v;
g[u].push_back(v);
g[v].push_back(u);
}
dfs(1,0);
dfs2(1,0);
cout<<ans<<endl;
}
int main(){
ios::sync_with_stdio(false);
cin.tie(nullptr);
int t = 1;
while(t--){
solve();
}
return 0;
}
FG.小红的陡峭值(五六)(easy-hard)
这里放一手ai✌的数学推理,简单来说还是贡献法求解。
最后贴上代码
#include <bits/stdc++.h>
using namespace std;
using LL = long long;
const int mod = 1e9+7;
LL kfc(LL x,LL y){
LL res = 1;
while(y){
if(y&1) res = x*res%mod;
y>>=1;
x = x*x%mod;
}
return res;
}
void solve(){
int n;
cin>>n;
vector<LL> a(n+1,0),fac(n+1,1);
for(int i=1;i<=n;i++) cin>>a[i];
for(int i=1;i<=n;i++){
fac[i] = (fac[i-1]*i)%mod;
}
sort(a.begin(),a.end());
LL sum = 0;
for(int i=1;i<=n;i++){
sum += a[i]*(2*i -n - 1);
if(sum<0) sum+=mod;
else sum = sum%mod;
}
sum = 2*sum%mod;
cout<<(sum*fac[n-1]%mod*kfc(fac[n],mod-2)%mod)%mod;
}
int main(){
ios::sync_with_stdio(false);
cin.tie(nullptr);
int t = 1;
while(t--){
solve();
}
return 0;
}