F Just a Joke

题意

博弈游戏,给一个无环图,每次可以删除一条边或者一个连通分量。

解析

由于无环连通,因此个点的连通分量必须是条边,当我们删除点的时候减少的边数和点数为

我们有两种操作,都是减少奇数个元素。

  1. 减少一条边
  2. 减少一个连通分量

我们最开始的元素总数为,假设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);
}