题目目录:F J I C
F题 Just a joke(就是个玩笑)

题目大意:A和B在玩一个游戏,游戏规则如下:开局给出一张有n个结点的图G,在每个回合可以进行如下操作:
1.选择G图中的一条边删除;
2.选择G图中的一个连通元素(包括单个点在内)删除。
A先手进行操作,进行最后一步操作的玩家获胜。
一个无向图的连通元素是指一个点集中每对结点用一条边连接,一个结点连接一条边,并且图中的其他结点没有边连接这个点集中的点。
举例,三个点构成的图,边的集合为{(1,2)(2,3)(1,3)},{1,2,3}是一个连通元素而{1,2}{1,3}不是。

思路:题目给出了点的个数和边的条数,不管是进行操作1还是操作2,减少的点和边的总个数必然是奇数个,所以只要看n-m的奇偶性即可

#include <bits/stdc++.h>
using namespace std;
int main(){
    int n,m;
    cin>>n>>m;
    int m0=m;
    while(m0--){
        int u,v;
        cin>>u>>v;
    }
    if((n-m)%2==0) cout<<"Bob";
    else cout<<"Alice";
}

J题 Average
题目大意:给出一个n*m的矩阵W和序列a,序列b。W(i,j)=a(i)+b(j),找出满足长≥x,宽≥y大小的平均值最大的子矩阵。输出最大的平均值。

思路:用二分法的思想分别找出序列a,序列b满足长度要求的平均值最大相加即可。二分法的思想是:假设一个可能的数,用数组里的值全部减去这个值,看是否存在一段长度符合要求且和大于0。二分在当l-r满足精度要求时停止。

#include <bits/stdc++.h>
using namespace std;
const int N=1e5+10;
const double eps=1e-7;
double a[N],b[N],sum[N];
bool check(double mid,int n,int m){
    for(int i=1;i<=n;i++) sum[i]=sum[i-1]+a[i]-mid;
    double minn=1e10;
    for(int i=m;i<=n;i++){
        minn=min(minn,sum[i-m]);//保证长度大于等于m
        if(sum[i]-minn>=0) return true;
    }
    return false;
}
double solve(int n, int m){//求序列长度大于m的子序列平均值最大 
    for(int i=1;i<=n;i++) scanf("%lf", &a[i]);
    double l=0,r=100010;
    while (r-l>eps){//保证精度符合要求 
        double mid=(l+r)/2;
        if(check(mid,n,m)) l=mid; 
        else r=mid;
    }
    return r;
}
int main() {
    int n,m,x,y;
    scanf("%d %d %d %d",&n,&m,&x,&y);
    printf("%.10lf", solve(n,x) + solve(m,y));
    return 0;
}

I题 Inverse Pair
题目大意:给出一个全排列序列a,可以对每个元素进行+1操作,也可以不加。求能获得的最小逆序数。

思路:当一个数x在x+1后面时,把x加1而x+1不变,这样就可以让逆序数减少一个。建一张图,如果x在x+1后则连一条边,最后会得到多条链,一条长度为b的链可以减少b/2个逆序数。

#include <bits/stdc++.h>
using namespace std;
const int maxn=2e5+7;
int tree[maxn],a[maxn],mp[maxn];
int n;   
void add(int x) {
    while(x<maxn) {
        tree[x]+=1;
        x+=((x)&-(x));
    }
}
int sum(int x) {
    int sum=0;
    while(x) {
        sum+=tree[x];
        x-=((x)&-(x));
    }
    return sum;
}
int main() {
    cin>>n;
    for(int i=1;i<=n;i++) cin>>a[i];
    long ans=0;
    for(int i=1;i<=n;i++){
        ans+=sum(maxn-1)-sum(a[i]);
        add(a[i]);
    }
    for(int i=n;i>0;i--) {
        if(mp[a[i]-1]) ans-=1;
        else mp[a[i]]=1;
    }
    cout<<ans<<endl;
    return 0;
}

C题 LCS
题目大意:要求构造三个字符串s1,s2,s3,使得它们俩俩公共子串长度为a,b,c。

思路:先构造公共子串长度最小的一段,然后再根据要求补全其他两子串的长度。如果此时构造的三个字符串长度有大于n的,说明不符合要求,输出NO;否则再用不同的字符(如xyz)分别补全三个字符串的长度到n。

#include <bits/stdc++.h>
using namespace std;
typedef long long LL;
int a,b,c,n;
int main(){
    cin>>a>>b>>c>>n;
    int m=min(min(a,b),c);
    string A,B,C;
    for(int i=1;i<=m;i++)    A+='a',B+='a',C+='a';
    for(int i=1;i<=a-m;i++)    A+='b',B+='b';
    for(int i=1;i<=b-m;i++)    B+='c',C+='c';
    for(int i=1;i<=c-m;i++)    A+='d',C+='d';
    if(A.size()>n||B.size()>n||C.size()>n){
        puts("NO");
        return 0;
    }
    while(A.size()<n)    A+='x';
    while(B.size()<n)    B+='y';
    while(C.size()<n)    C+='z';
    cout<<A<<endl;
    cout<<B<<endl;
    cout<<C<<endl;
    return 0;
}