目录

牛客周赛-Round-84

牛客周赛 Round 84

这场比赛确实比以前的水了很多,不过不影响,有学习意义即可

https://i-blog.csdnimg.cn/direct/6cc439833f3d421abd017d606210fdb1.png

思路:直接判断三个数是否相同,想通就输出Yes,否则就是No

#include<bits/stdc++.h>
using namespace std;
int a,b,c;
signed main()
{
    cin>>a>>b>>c;
    if(a==b&&b==c)
    {
        cout<<"Yes";
    }
    else
    {
cout<<"No";
    }
}

https://i-blog.csdnimg.cn/direct/de46d69a45404a9cbcb42e37f8053bb8.png

思路:这题明显不是考验排列组合的,因为要使得a[i]不同,也就说明最多只有两种,从大到小一次,从小到大一次,然后直接去统计两个数之间的差值累加和即可

#include<bits/stdc++.h>
using namespace std;
int n;
int a[105];
int cnt[105];
signed main()
{
    cin>>n;
    set<int> s;
    for(int i=1;i<=n;i++)
    {
    	cin>>a[i];
    	s.insert(a[i]);
    	cnt[a[i]]++;
	}
	sort(a+1,a+1+n);
	int ans=0;
	
	int cha=a[1];
	int sum=0;
	for(int c:s)
	{
		sum+=c-cha;
		cha=c;
	}
	if(s.size()==1)
	ans=1;
    else
    ans=2;
	cout<<ans<<" "<<sum;
    return 0;
}

https://i-blog.csdnimg.cn/direct/a910a2c288a147ceab0ef5893738e445.png

思路:可以用前缀和,先把每两两之间的差值算出来,然后用前缀和数组pre去统计从第1个差值到第i个差值的总和,然后就可以跑一遍从k+1到n,去统计pre[i]-pre[i-k]的累加和

#include<bits/stdc++.h>
using namespace std;
#define int long long
int n,k;
string s;
int cha[1000006];
int pre[1000006];
signed main()
{
	cin>>n>>k;
	cin>>s;
	s=' '+s;
	for(int i=2;i<=n;i++)
	{
		cha[i]=abs(s[i]-s[i-1]);
	}
	if(k==2)
	{
		int sum=0;
		for(int i=2;i<=n;i++)
		{
			sum+=cha[i];
		}
		cout<<sum<<"\n";
		return 0;
	}
	int sum=0;
	for(int i=2;i<=n;i++)
	{
		
		pre[i]=pre[i-1]+cha[i];
		
	}
	k-=1;
	for(int i=k+1;i<=n;i++)
	{
		
		sum+=pre[i]-pre[i-k];
	}
	cout<<sum;
	return  0;
}

https://i-blog.csdnimg.cn/direct/858805b19df14fc897a74d4ed7f9bdf4.png 思路:这题一看题就是属于 带权树重心 类问题了,题目中说是将一整颗树变成两棵树,也就是说,我们只需要去先去求到一整棵树的权值,然后 abs(用整棵树的权值-当前结点的权值-abs(当前结点的父节点-当前节点)

,数学公式可以这么去表示,那么我们应该先去跑dfs,先去把整棵树每个点的权值求出来,叶子结点的权值是0,然后我们在dfs遍历中找到每个点权值最大的子节点的编号,然后最后dfs跑完遍历一遍,去寻找绝对值后的最小值,我们在遍历过程中要去跳过叶子结点

#include<bits/stdc++.h>
using namespace std;
#define int long long
int n;
int u,v;
vector<int> e[200005];
int ans[200005];
int mx[200005];
void dfs(int v,int fa)
{
	int maxn=0;
	for(int u:e[v])
	{
		if(u!=fa)
		{
			dfs(u,v);
			ans[v]+=ans[u]+abs(v-u);
			if(ans[u]>maxn)
			{
				maxn=ans[u];
				mx[v]=u;
			}
			else if(ans[u]==maxn)
			{
				mx[v]=min(mx[v],u);
			}
		}
	}
}
signed main()
{
	cin>>n;
	if(n==1)
	{
		cout<<0;
		return 0;
	}
	for(int i=1;i<=n-1;i++)
	{
		cin>>u>>v;
		e[u].push_back(v);
		e[v].push_back(u);
	}
	dfs(1,0);
	int sum=ans[1];
	int minn=1e12;
	for(int i=1;i<=n;i++)
	{
		
		if(ans[i]!=0)
		{
			minn=min(minn,abs(ans[mx[i]]-(sum-ans[mx[i]]-abs(i-mx[i]))));
		
		}
	}
	cout<<minn;
	return 0;
}

https://i-blog.csdnimg.cn/direct/22d620d881b140be9fa7e22577c8e5f3.png

思路:我们可以现将这个题转换为两个数相邻的概率,如果先从数组中除去两个数,那么还剩下n-2个数,然后出现的概率为(n-2)!,如果将捆绑的两个数插入有2*(n-1)种可能,除以总的情况n!,那么说明每两个数相邻的概率为2/n;

那么根据题目来说,我们应该去求2/n*(a[j]-a[i]){1<=i<j<=n};但是这样的时间复杂度为O(n^2),还是太高了,如何降低时间复杂度呢?

直接用排序+前缀和去求任意两个数之间的绝对值之差即可,时间复杂度降为O(n)刚好满足题意

#include<bits/stdc++.h>
using namespace std;
#define int long long
const int M = 1e9+7;

int a[200005];

long long pow(long long a, int k) {
  if (k == 0) return 1;
  return pow(a*a%M, k/2)*(k%2?a:1)%M;
}

int main() {
  int n;
  cin>>n;
  for (int i=1; i<=n; i++)
    cin>>a[i];
  sort(a+1, a+1+n);
  long long ans = 0, sum = 0;
  for (int i=1; i<=n; i++)
    ans += (long long)a[i]*(i-1) - sum, ans %= M, sum += a[i];
  cout<<ans*2%M*pow(n, M-2)%M<<'\n';
  
  return 0;
}