F Just a Joke
题意
博弈游戏,给一个无环图,每次可以删除一条边或者一个连通分量。
解析
由于无环连通,因此个点的连通分量必须是
条边,当我们删除点的时候减少的边数和点数为
。
我们有两种操作,都是减少奇数个元素。
- 减少一条边
- 减少一个连通分量
我们最开始的元素总数为,假设A先手,使得K减去一个奇数,变为一个偶数,随后B出手,使得这个偶数又变为奇数。因此每一轮次操作后,结果的奇偶性不变,而操作后元素为0才算胜利,因此只要判断元素总和的奇偶性即可。
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
int main() {
int n,m;
cin>>n>>m;
if((n+m)&1)cout<<"Alice";
else cout<<"Bob";
}i Inverse Pair
题意
对序列的元素+1或者+0,构造出新的序列,求经过这个操作后,此序列的最小逆序对。
题解一(二分+归并排序
我们对元素+1能消除哪种类型的逆序对呢?
所以我们只需要在归并求逆序对的时候,利用二分查找找到上述的个数,然后通过+1的操作,让这些逆序对消除。最后结果就是原数组的逆序对减去被消除的逆序对的个数。
但是,如果我们对进行加1的操作去消除逆序对,那么我们的
就不能再进行加一了,本题解中称为**固定
**,被固定的值就不能带来任何收益,我们将其收益标记为0。因此我们要进行一个取舍,选择收益最大的。
假设 4 4 3 2 ,最优的策略是对3进行+1,而不是对2进行+1,因此我们只需要判断+1后能带来的收益哪个更大,被固定的值就不能带来任何收益,因此将其收益标记为0。
时间复杂度
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N=200000+5;
int a[N];
int temp[N];
long long int ans=0;
map<int,ll>mp;
void merge_sort(int a[],int l,int r){
if(l>=r)return;
int mid=l+r>>1;
merge_sort(a,l,mid);
merge_sort(a,mid+1,r);
int idx=l;
int i=l,j=mid+1;
while(i<=mid && j<=r){
if(a[i]>a[j]){
ans+=mid-i+1;
// i~mid [i,mid]找到大于1的个数
int index=upper_bound(a+i,a+mid+1 ,a[j]+1)-a;
int value=index-i;
mp[a[j]]+=(value);
temp[idx++]=a[j++];
}
else{
temp[idx++]=a[i++];
}
}
while(i<=mid){
temp[idx++]=a[i++];
}
while(j<=r){
temp[idx++]=a[j++];
}
for(int k=l;k<=r;k++)
a[k]=temp[k];
}
int main(){
int n ;
cin>>n;
for(int i=1;i<=n;i++){
cin>>a[i];
}
merge_sort(a,1,n);
ll res=0;
for(auto i:mp){
if(mp.find(i.first+1)!=mp.end() ){
if(i.second >= mp[i.first+1]){
mp[i.first+1]=0;//固定
res+=i.second;
}
}
else{//不需要进行取舍,直接消除。
res+=i.second;
}
}
cout<<ans-res<<endl;
}
题解二(建图
视频讲解的做法。建树状数组
C LCS
题意
构造题,给出
讨论一下No的情况
先求出LCS最小值作为公共部分,每一个LCS都减去
,最长重合部分为
(a,b,c其中一个已经为0),如果此条件不成立,就无解。
解析
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
int main(){
int a,b,c,n;
cin>>a>>b>>c>>n;
int mi=min({a,b,c});//公共部分
a-=mi,b-=mi,c-=mi;
if(a+b+c+mi>n){
cout<<"NO";
}
else{
string aa(mi,'a'),bb(mi,'a'),cc(mi,'a');
for(int i=1;i<=a;i++){
aa+='b';
bb+='b';
}
for(int i=1;i<=b;i++){
bb+='c';
cc+='c';
}
for(int i=1;i<=c;i++){
aa+='d';
cc+='d';
}
while(aa.length()<n)aa+='x';
while(bb.length()<n)bb+='y';
while(cc.length()<n)cc+='z';
cout<<aa<<endl<<bb<<endl<<cc<<endl;
}
}
J Average
题意
两个数组构建矩阵
,求此矩阵的平均值最大的子矩阵
解析
的平均值公式为
由于,所以我们转换一下。
再转换一下
化简得
我们观察式子可知,答案为两个序列的子序列最大平均值,因此我们二分,分别求出两个序列的最大平均值,然后相加即可。
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
double eps=1e-7;
const int N=1e5+5;
int a[N],b[N];
double pa[N];
int n,m,x,y;
bool check(int *a,int n,double avg,int x){
for(int i=1;i<=n;i++){
pa[i]=pa[i-1]+a[i]-avg;
}
double mi=0,mx=INT_MIN;
for(int i=x;i<=n;i++){
int j=i-x;
mi=min(mi,pa[j]);
mx=max(mx,pa[i]-mi);
}
return mx>=0;
}
double get_ans(int *a,int n,int x){
double l=0,r=1e7;
while(r-l>eps){
double mid=(l+r)/2;
check(a,n,mid,x)?l=mid:r=mid;
}
return l;
}
int main(){
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];
double ans=get_ans(a,n,x)+get_ans(b,m,y);
printf("%.10lf",ans);
}

京公网安备 11010502036488号