2021牛客暑期多校训练营4 部分题解

C.LCS

  • 题意
    给出 a , b , c a,b,c a,b,c三个整数,构造三个字符串 s 1 , s 2 , s 3 s_1,s_2,s_3 s1,s2,s3满足 L C S ( s 1 , s 2 ) = a , L C S ( s 2 , s 3 ) = b , L C S ( s 1 , s 3 ) = c LCS(s_1,s_2)=a,LCS(s_2,s_3)=b,LCS(s1_,s_3)=c LCS(s1,s2)=a,LCS(s2,s3)=b,LCS(s1,s3)=c

  • 解题思路
    贪心构造即可,找到最小的那个,那肯定是每个人都有的,然后确定好是哪个字符串之后根据其他的值一次构造。注意情况判断。代码 1 1 1比较麻烦,代码 2 2 2为通解:考虑这三个串互相的 L C S LCS LCS x , y , z x,y,z x,y,z, 且 x > = y > = z x>=y>=z x>=y>=z,显然如果 x + y − n > z x+y-n>z x+yn>z, 则无解, 所以一定有 x + y − n < = z x+y-n<=z x+yn<=z,我们先给这三个串加上一个 z z z a a a的前缀, 然后就变成了一个 x − z , y − z , 0 , n − z x-z,y-z,0,n-z xz,yz,0,nz的同类问题。因为 x + y − n < = z x+y-n<=z x+yn<=z, 所以 ( x − z ) + ( y − z ) < = n − z (x-z)+(y-z)<=n-z (xz)+(yz)<=nz, 所以我们给前两个串一起放上 x − z x-z xz b b b, 后两个串一起放上 y − z y-z yz c c c即可。

  • AC代码1

/**
  *@filename:C
  *@author: pursuit
  *@csdn:unique_pursuit
  *@email: 2825841950@qq.com
  *@created: 2021-07-26 12:01
**/
#include <bits/stdc++.h>

using namespace std;

typedef long long ll;
const int N = 100000 + 5;
const int P = 1e9+7;

int n,a[3],pos[3];
string s[3];
bool cmp(int i,int j){
    return a[i] < a[j];
}
void solve(){
}
int main(){
    for(int i = 0; i < 3; ++ i)cin >> a[i], pos[i] = i,s[i] = "";
    cin >> n;
    for(int i = 0; i < 3; ++ i){
        if(a[i] > n){
            cout << "NO" << endl;
            return 0;
        }
    }
    sort(pos,pos + 3,cmp);
    for(int i = 0; i < n; ++ i){
        if(i < a[pos[0]]){
            s[pos[0]] += "a",s[(pos[0] + 1) % 3] += "a";
        }
        else{
            s[pos[0]] += "b",s[(pos[0] + 1) % 3] += "c";
        }
    }
    //剩下那个是(pos[0] + 2) % 3;
    int idx = (pos[0] + 2) %  3;
    for(int i = 0; i < a[idx]; ++ i){
        s[idx] += s[(idx + 1) % 3][i];
    }
    if(n - a[idx] >= a[(idx + 2) % 3] - a[pos[0]]){
        for(int i = a[idx]; i < n; ++ i){
            if(i - a[idx] < a[(idx + 2) % 3] - a[pos[0]]){
                s[idx] += s[(idx + 2) % 3][i];
            }
            else{
                s[idx] += "d";
            }
        }
    }
    else{
        cout << "NO" << endl;
        return 0;
    }
    for(int i = 0; i < 3; ++ i){
        cout << s[i] << endl;
    }
    solve();
    return 0;
}
  • AC代码2
/**
  *@filename:C
  *@author: pursuit
  *@csdn:unique_pursuit
  *@email: 2825841950@qq.com
  *@created: 2021-07-26 12:01
**/
#include <bits/stdc++.h>

using namespace std;

typedef long long ll;
const int N = 100000 + 5;
const int P = 1e9+7;

int n,a,b,c;
string s1,s2,s3;
void solve(){
    int minn = min(a,min(b,c));
    //判断情况。
    if(a + b - minn > n || a + c - minn > n || b + c - minn > n){
        cout << "NO" << endl;
    }
    else{
        for(int i = 0; i < minn; ++ i){
            s1 += "a";
            s2 += "a";
            s3 += "a";
        }
        for(int i = 0; i < a - minn; ++ i){
            s1 += "b";
            s2 += "b";
        }
        for(int i = 0; i < b - minn; ++ i){
            s2 += "c";
            s3 += "c";
        }
        for(int i = 0; i < c - minn; ++ i){
            s1 += "d";
            s3 += "d";
        }
        while(s1.size() < n)s1 += "e";
        while(s2.size() < n)s2 += "f";
        while(s3.size() < n)s3 += "g";
        cout << s1 << endl;
        cout << s2 << endl;
        cout << s3 << endl;
    }
}
int main(){
    cin >> a >> b >> c >> n;
    solve();
    return 0;
}

F.Just a joke

  • 题意
    有一个图,现在 A l i c e , B o b Alice,Bob Alice,Bob进行一个游戏,每个人进行一个操作:删除一条边或者删除一个连通块(连通块中的点也会被删掉)。 A l i c e Alice Alice先进行。当有人不能进行操作时则输掉比赛。问谁能赢得比赛。

  • 解题思路
    思维题。只有两种操作,删除一条边,边数-1。删除一个连通分量,点数-k,边数-(k-1)。所以每次操作都会影响边和点的和的奇偶性。故只需要判断奇偶性质即可。

  • AC代码

/**
  *@filename:F
  *@author: pursuit
  *@csdn:unique_pursuit
  *@email: 2825841950@qq.com
  *@created: 2021-07-26 12:20
**/
#include <bits/stdc++.h>

using namespace std;

typedef long long ll;
const int N = 110 + 5;
const int P = 1e9+7;

int n,m,u,v;
void solve(){
    
    if((n + m) & 1){
        cout << "Alice" << endl;
    }
    else{
        cout << "Bob" << endl;
    }
}
int main(){
    cin >> n >> m;
    for(int i = 1; i <= m; ++ i){
        cin >> u >> v;
    }
    solve();
    return 0;
}

I.Inverse Pair

  • 题意
    给你一个序列 a a a,其为 1 1 1~ n n n的排列,权重为 a a a的逆序对数。你可以构造一个序列 b b b,其中 b b b的元素取值只能为 0 , 1 0,1 0,1。问新序列 c c c的最小权重。

  • 解题思路
    我们构造序列 b b b即为了减少逆序对的个数,那么如果 a i a_i ai后面存在 a i + 1 a_{i+1} ai+1那么我们就可以实现让 b i + 1 = 1 b_{i+1}=1 bi+1=1从而减小,其他则别无它法。所以我们可以利用树状数组来实现,统计序对个数,再用总逆序对数减去得到真正的逆序对数,然后我们通过数组 b b b记录元素位置,从而判断相邻元素的坐标情况。

  • AC代码

/**
  *@filename:I
  *@author: pursuit
  *@csdn:unique_pursuit
  *@email: 2825841950@qq.com
  *@created: 2021-07-26 13:28
**/
#include <bits/stdc++.h>

using namespace std;

typedef long long ll;
const int N = 200000 + 5;
const int P = 1e9+7;

int n,a[N],b[N],c[N];
ll sum;//统计逆序对个数。
//利用树状数组统计逆序对。
int lowbit(int x){
    return x & (-x);
}
void add(int x){
    for(int i = x; i <= n; i += lowbit(i)){
        c[i] ++;
    }
}
int query(int x){
    int res = 0;
    for(int i = x; i >= 1; i -= lowbit(i)){
        res += c[i];
    }
    return res;
}
void solve(){
    sum = 1LL * n * (n - 1) / 2 - sum;//应有的逆序对。
    int idx = 1;
    while(idx < n){
        if(b[idx] > b[idx + 1])sum --,idx += 2;
        else idx ++;
    }
    printf("%lld\n", sum);
}
int main(){
    scanf("%d", &n);
    for(int i = 1; i <= n; ++ i){
        scanf("%d", &a[i]);
        sum += query(a[i]);
        add(a[i]);
        b[a[i]] = i;//记录其位置。
    }
    solve();
    return 0;
}

J.Average

  • 题意
    给定一个矩阵,求子矩阵的最大平均值和。

  • 解题思路
    ∑ i = l 1 r 1 ∑ j = l 2 r 2 a i + b j ( r 1 − l 1 + 1 ) ( r 2 − l 2 + 1 ) \frac{\sum_{i=l_1}^{r_1}\sum_{j={l_2}}^{r_2}{a_i+b_j}}{(r_1-l_1+1)(r_2-l_2+1)} (r1l1+1)(r2l2+1)i=l1r1j=l2r2ai+bj​​= ∑ i = l 1 r 1 a i ( r 2 − l 2 + 1 ) + ∑ j = l 2 r 2 b j ( r 1 − l 1 + 1 ) ( r 1 − l 1 + 1 ) ( r 2 − l 2 + 1 ) \frac{\sum_{i=l_1}^{r_1}a_i(r_2-l_2+1)+\sum_{j={l_2}}^{r_2}{b_j(r_1-l_1+1)}}{(r_1-l_1+1)(r_2-l_2+1)} (r1l1+1)(r2l2+1)i=l1r1ai(r2l2+1)+j=l2r2bj(r1l1+1)​​= ∑ i = l 1 r 1 a i ( r 1 − l 1 + 1 ) + ∑ j = l 2 r 2 b j ( r 2 − l 2 + 1 ) \frac{\sum_{i=l_1}^{r_1}a_i}{(r_1-l_1+1)}+\frac{\sum_{j={l_2}}^{r_2}{b_j}}{(r_2-l_2+1)} (r1l1+1)i=l1r1ai+(r2l2+1)j=l2r2bj​​​= max ⁡ a ˉ + max ⁡ b ˉ \max\bar a+\max \bar b maxaˉ+maxbˉ,故即求 a , b a,b a,b​区间最大平均值,题中约束 a a a区间长度至少为 x x x b b b区间长度至少为 y y y,这是典中典的问题,我们二分区间平均值去判断其是否可行,这样我们可以避免除法的干扰,利用前缀和实现即可。

  • AC代码

/**
  *@filename:J
  *@author: pursuit
  *@csdn:unique_pursuit
  *@email: 2825841950@qq.com
  *@created: 2021-07-26 13:51
**/
#include <bits/stdc++.h>

using namespace std;

typedef long long ll;
const int N = 100000 + 5;
const int P = 1e9+7;

int n,m,x,y;
double a[N],sum[N];
double solve(int n,int len){
    //len为区间最小长度。
    for(int i = 1; i <= n; ++ i){
        scanf("%lf", &a[i]);
    }
    double l = -1e5,r = 1e5,mid;
    while(r - l > 1e-7){
        mid = (l + r) / 2.0;
        //cout << mid << " ";
        for(int i = 1; i <= n; ++ i){
            sum[i] = sum[i - 1] + a[i] - mid;
        }
        double minn = 1e6,maxx = -1e6;
        for(int i = len; i <= n; ++ i){
            minn = min(minn,sum[i - len]);//不断维护左端最小值。
            maxx = max(maxx,sum[i] - minn);//相减得到最大值。
        }
        if(maxx >= 0){
            l = mid;
        }
        else{
            r = mid;
        }
    }
    return r;
}
int main(){
    scanf("%d%d%d%d", &n, &m, &x, &y);
    printf("%.8lf\n",solve(n,x) + solve(m,y));
    return 0;
}
posted @ 2022-03-26 16:48  unique_pursuit  阅读(24)  评论(0)    收藏  举报