2021牛客暑期多校训练营4,签到题CFIJ

Posted 小哈里

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了2021牛客暑期多校训练营4,签到题CFIJ相关的知识,希望对你有一定的参考价值。

题号 标题 已通过代码 通过率 团队的状态
A Course 点击查看 18/52 未通过
B Sample Game 点击查看 122/281 未通过
C LCS 点击查看 1118/2877 通过 (字符串构造)
D Rebuild Tree 点击查看 23/97 未通过
E Tree Xor 点击查看 151/825 未通过
F Just a joke 点击查看 1448/3161 通过 (结论,奇偶性)
G Product 点击查看 72/226 未通过
H Convolution 点击查看 43/117 未通过
I Inverse Pair 点击查看 1306/3459 通过 (逆序对,树状数组)
J Average 点击查看 1057/3991 通过 (二分答案,区间/子数组最大平均值,)

C LCS

题意:

  • 给出a,b,c,n,构造3个长度为n的字符串s1,s2,s3满足LCS(s1,s2)=a,LCS(s2,s3)=b, LCS(s3,s1)=c

思路:

  • 先给这三个串加上一个 z 个 a 的前缀, 然后就变成了一个 x-z,y-z,0,n-z 的同类问题,因为 x+y-n<=z, 所以 (x-z)+(y-z)<=n-z, 所以我们给前两个串一起放上 x-z 个 b, 后两个串一起放上 y-z 个 c即可。如果 x+y-n>z, 则无解。
#include<bits/stdc++.h>
using namespace std;
int main(){
    int a, b, c, n;  cin>>a>>b>>c>>n;
	int l1 = min(a,min(b,c)),l2=1e9+10, l3=-1;
	if(a==l1)l2=min(b,c),l3=max(b,c);
	if(b==l1)l2=min(a,c),l3=max(a,c);
	if(c==l1)l2=min(a,b),l3=max(a,b);
	int ok = l1+l2-l1+l3-l1;
	if(ok>n){ cout<<"NO\\n"; return 0;}
	string s1 = string(l1, 'a');  string s2=s1, s3=s1;
	l2 -= l1, l3-=l1; a -= l1; b-=l1; c-=l1;
	if(l2==a)s1+=string(l2,'b'), s2+=string(l2,'b'), s3+=string(l2,'q');
	if(l2==b)s2+=string(l2,'b'), s3+=string(l2,'b'), s1+=string(l2,'q');
	if(l2==c)s1+=string(l2,'b'), s3+=string(l2,'b'), s2+=string(l2,'q');
	if(l3==a)s1+=string(l3,'c'), s2+=string(l3,'c'), s3+=string(l3,'p');
	if(l3==b)s2+=string(l3,'c'), s3+=string(l3,'c'), s1+=string(l3,'p');
	if(l3==c)s1+=string(l3,'c'), s3+=string(l3,'c'), s2+=string(l3,'p');
	int len = n-l1-l2-l3;
	s1 += string(len, 'x'); s2 += string(len, 'y'); s3 += string(len, 'z');
	cout<<s1<<"\\n";  cout<<s2<<"\\n";  cout<<s3<<"\\n";
    return 0;
}

F Just a joke

题意:

  • 给出一个有n个点的无向图,A和B轮流操作,A先手。每次操作可以删除图中的一条边或一个联通分量,求谁能获胜。

思路:

  • 对于第一种操作, 会使得边数 -1,对于第二种操作, 会使得点数 -k, 边数 -(k-1),任何一种操作都会使得点数+边数的和减少一个奇数, 所以答案只跟 n+m 的奇偶性有关
#include<bits/stdc++.h>
using namespace std;
int main(){
	int n, m;  cin>>n>>m;
	cout<<((m+n)%2?"Alice":"Bob")<<"\\n";
	return 0;
}

I Inverse Pair

题意:

  • 给出一个长为n的排列a,求构造一个长为n的01序列b,满足c=a+b的逆序对个数最小,输出逆序对个数。

思路:

  • 考虑一下什么情况下 +1 这个操作会让逆序对变少
  • 如果 x 在 x+1 的后面, 则我们把 x 这个数+1, x+1 不变, 就能让逆序对减少 1
  • 那考虑建一张图, 如果 x 在 x+1 后面则连边 (x,x+1), 最后会得出若干条链, 一条长度为 L 的链我们最多用它减少 L/2 个逆序对
  • 时间复杂度 O(nlogn)
#include<bits/stdc++.h>
using namespace std;
typedef long long LL;
const int maxn = 2e5+10;

//树状数组求逆序对
int n, bit[maxn];
void add(int x, int v){for(int i=x;i<=n;i+=i&(-i))bit[i]+=v;}
int query(int x){int ans=0;for(int i=x;i>0;i-=i&(-i))ans+=bit[i];return ans;}

int vis[maxn]; LL ans;

int main(){
	cin>>n;
	for(int i = 1; i <= n; i++){
		int x;  cin>>x;
		if(vis[x+1])x++;
		ans += i-1-query(x),add(x,1);
		vis[x] = 1;
	}
	cout<<ans<<"\\n";
	return 0;
}

J Average

题意:

  • 给出长为n,m的序列ai和bi,定义nm的矩阵wij=ai+bj,求一个大小>=xy的子矩阵的平均值最大。

思路:

  • 因为n的范围为1e5,直接n*m计算出wij就已经超时,所以考虑贡献。从wij的矩阵平均值公式可以推出平均值分别为a和b的最大平均值相加。
  • 所以答案就是分别对 a 和 b 对应的区间求平均值然后加起来,问题转化成了 : 找 a 的一个长度至少为 x 的平均值最大的子区间和 b 的一个长度至少为 y 的平均值最大的子区间
  • 二分答案 S 后, 令 a[i] 变成 a[i]-S, 则问题变成求是否有和 >0 的长度至少为 x 的子区间, 这个可以 O(n) 计算 时间复杂度 O(nlogW)
#include<bits/stdc++.h>
using namespace std;
typedef long long LL;
const int maxn = 4e5+10;
const double esp = 1e-10;

double a[maxn], b[maxn], tmp[maxn];
bool check(double num[], double avg, int n, int k){
	for(int i = 1; i <= n; i++)tmp[i]=num[i]-avg;
	for(int i = 1; i <= n; i++)tmp[i]+=tmp[i-1];
	double mi = 1e5+10;
	for(int i=k; i <= n; i++){
		mi = min(mi,tmp[i-k]);
		if(tmp[i]-mi>=0)return true;
	}
	return false;
}
double solve(double num[], int n, int k){
	double l = -1e5, r = 1e5;
	while(l+esp <= r){
		double mid = (l+r)/2.0;
		if(check(num, mid, n, k))l = mid;
		else r = mid;
	}
	return l;
}

int main(){
	int n, m, x, y;  cin>>n>>m>>x>>y;
	for(int i = 1; i <= n; i++)cin>>a[i];
	for(int i = 1; i <= m; i++)cin>>b[i];
	printf("%.7lf", solve(a,n,x)+solve(b,m,y));
	return 0;
}

以上是关于2021牛客暑期多校训练营4,签到题CFIJ的主要内容,如果未能解决你的问题,请参考以下文章

2021牛客暑期多校训练营8,签到题ADEK

2021牛客暑期多校训练营1, 签到题DFBG

2021牛客暑期多校训练营2,签到题CDFKI

2021牛客暑期多校训练营7,签到题FHI

2021牛客暑期多校训练营10,签到题FH

2021牛客暑期多校训练营5,签到题BDHJK