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的主要内容,如果未能解决你的问题,请参考以下文章