牛客 周赛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\)

\[ans=\sum_{i=1}^{n-1} a_{i+1}-a_i=a_n-a_1 \]

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;
}

前缀和搞定

需要计算的总和可以表示为

\[ans=\sum_{j=1}^{n-k+1}\sum_{i=j}^{j+k-2}\lvert a_{i+1}-a_i\rvert \]

使用 \(d_i\) 表示 \(\lvert a_{i+1}-a_i\rvert\) ,计算相邻元素差的绝对值,\(p_i\) 表示前 \(i\) 个字符组成的子串的陡峭值

可以整理为

\[ans=\sum_{i=k-1}^n p_i-p_{i-k} \]

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\),需要通过总陡峭值代换计算

\[ans=\lvert d_v-(sum-d_v-w)\rvert=sum-2*d_v-w \]

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)\) ,即考虑剩余元素的全排列,再插入这对元素的方案数

答案可以被表示为

\[ans=\sum_{i=1}^{n} \sum_{j=1}^{n} A_{n-2}^{n-2}*(n-1)*d_{i,j}\ /\ A_{n}^{n}\\ =\sum_{i=1}^{n} \sum_{j=1}^{n} (n-1)!*d_{i,j}\ /\ n!=\frac{\sum_{i=1}^{n} \sum_{j=1}^{n} d_{i,j}}{n} \]

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=2*\sum_{i=1}^{n}\sum_{j=1}^{i-1} (a_i-a_j)=2*\sum_{i=1}^n(a_i*(i-1)-\sum_{j=1}^{i-1}a_j) \]

其中 \(\sum_{j=1}^{i-1}\) 可以利用前缀和 \(O(1)\) 得到,所以总计算的时间复杂度为 \(O(n)\),总时间复杂度为 \(O(n\log n)\)

posted @ 2025-03-16 21:19  才瓯  阅读(14)  评论(0)    收藏  举报