Codeforces Round #538 (Div. 2) 题解

A. Got Any Grapes?

  • 解题思路

    贪心,先分给只吃绿葡萄的,再分给不吃黑葡萄的。所以我们只需要判断 x ≤ a   a n d   a − x + b ≥ y   a n d   a + b + c ≥ x + y + z x\leq a \ and \ a-x + b \geq y \ and \ a + b + c \geq x + y + z xa and ax+by and a+b+cx+y+z​即可。

  • AC代码

/**
  *@filename:A
  *@author: pursuit
  *@created: 2021-08-16 14:14
**/
#include <bits/stdc++.h>
#define debug(a) cout << "debug : " << (#a)<< " = " << a << endl

using namespace std;

typedef pair<int,int> pii;
typedef long long ll;
const int N = 1e5 + 10;
const int P = 1e9 + 7;
const int INF = 0x3f3f3f3f;

int x,y,z,a,b,c;
void solve(){
    if(x > a || y > a - x + b || x + y + z > a + b + c){
        puts("NO");
    }
    else{
        puts("YES");
    }
}
int main(){	
    cin >> x >> y >> z >> a >> b >> c;
    solve();
    return 0;
}

B. Yet Another Array Partitioning Task

  • 解题思路

    我们能将元素分成 k k k组,那么我们取的元素自然能取到前 k × m k\times m k×m大的元素,所以我们只需要统计这前 k × m k\times m k×m​的元素的和即是答案,那么怎么确定分组呢?我们这些元素标记其在原数组中,然后顺序遍历统计,当达到 m m m个标记的元素的时候说明我们可以进行分组了,输出此时右端点编号即可。

  • AC代码

/**
  *@filename:B
  *@author: pursuit
  *@created: 2021-08-16 14:19
**/
#include <bits/stdc++.h>
#define debug(a) cout << "debug : " << (#a)<< " = " << a << endl

using namespace std;

typedef pair<int,int> pii;
typedef long long ll;
const int N = 2e5 + 10;
const int P = 1e9 + 7;
const int INF = 0x3f3f3f3f;

int n,m,k;
bool vis[N];//判断是否为前k * m大。
int a[N],pos[N];
bool cmp(int i,int j){
    return a[i] > a[j];
}
void solve(){
    //我们贪心分组自然能获取前k * m大的元素。
    sort(pos + 1,pos + 1 + n,cmp);
    ll ans = 0;
    for(int i = 1; i <= k * m; ++ i){
        ans += a[pos[i]];
        vis[pos[i]] = true;
    }
    printf("%lld\n", ans);
    int cnt = 0;
    for(int i = 1; i <= n; ++ i){
        cnt += vis[i];
        if(cnt == m && k > 1){
            printf("%d ", i);
            cnt = 0;
            k --;
        }
    }
    puts("");
}
int main(){	
    scanf("%d%d%d", &n, &m, &k);
    for(int i = 1; i <= n; ++ i){
        scanf("%d", &a[i]);
        pos[i] = i;
    }
    solve();
    return 0;
}

C. Trailing Loves (or L’oeufs?)

  • 解题思路

    根据唯一分解定理,KaTeX parse error: Got function '\inf' with no arguments as superscript at position 15: b=\prod_{i=1}^\̲i̲n̲f̲ ̲p_1^{a_i},其中p_i…​​​​。而我们发现,当 n % b = 0 n \% b=0 n%b=0​​​​时,说明存在尾随零,那么我们将 n n n​​​​也分解得到KaTeX parse error: Got function '\inf' with no arguments as superscript at position 15: n=\prod_{i=1}^\̲i̲n̲f̲ ̲p_1^{b_i},其中p_i…​​​​​。那么我们发现,尾随零的个数实际上就是 min ⁡ { b i / a i } \min\{b_i/a_i\} min{bi/ai}​,因为这样才能表示成 n = x × b k n=x\times b^k n=x×bk​, k k k​就是尾随零的个数。

    清楚了这个,那么回到问题本身,现在我们需要处理 n ! n! n!​​​,这该怎么办呢 ? ? ?​这实际上是一个典中典的问题,​由于 n ! = ∏ i = 1 n i n!=\prod_{i=1}^ni n!=i=1ni​​​​,​​所以存在 x , 2 x , 3 x , 4 x … x,2x,3x,4x\dots x,2x,3x,4x分布在其中,从前往后不断增加,且间距都为 x x x。所以我们可以利用 n n n​​​​不断除以它们的系数递推累加因子数量即可,每个块的数量都是 n / x n/x n/x

    至此,我们遍历 b b b的质因子获取 n ! n! n!的质因子数量,然后得到 b i / a i b_i/a_i bi/ai,取最小值即可。

  • AC代码

/**
  *@filename:C
  *@author: pursuit
  *@created: 2021-08-16 14:39
**/
#include <bits/stdc++.h>
#define debug(a) cout << "debug : " << (#a)<< " = " << a << endl

using namespace std;

typedef pair<int,int> pii;
typedef long long ll;
const int N = 1e5 + 10;
const int P = 1e9 + 7;
const int INF = 0x3f3f3f3f;

ll n,b;
map<ll,ll> p;//存储b的质因子及其数量。
ll cal(ll n, ll x){
    ll res = 0;
    while(n){
        res += n / x;
        n /= x;
    }
    return res;
}
void solve(){
    ll temp = sqrt(b);
    for(ll i = 2; i <= temp; ++ i){
        if(b % i == 0){
            ll cnt = 0;
            while(b % i == 0){
                cnt ++;
                b /= i;
            }
            p[i] = cnt;  
        }
    }
    if(b > 1){
        p[b] ++;
    }
    ll ans = 1e18;
    for(auto &x : p){
        //cout << x.first << endl;
        ans = min(ans,cal(n,x.first) / x.second);
    }
    cout << ans << endl;
}
int main(){	
    cin >> n >> b;
    solve();
    return 0;
}

D. Flood Fill

  • 解题思路

    对于区间 [ l , r ] [l,r] [l,r]​​,因为我们只能处理连通分量进行变色,所以如果进行修改,那么肯定是根据边界修改的,要么全变为l处的颜色,要么全变为r处的颜色,因为这样才具有拓展性,使得连通分量向外拓展,这也是贪心的想法。

    从而,这即符合了区间 d p dp dp的思想,即可进行区间分离区间合并且能保证局部最优解构成全局最优解。

    所以设 d p [ l ] [ r ] [ x ] dp[l][r][x] dp[l][r][x]​​​表示区间 [ l , r ] [l,r] [l,r]​​​全变为 x x x​​​处的颜色的最小操作次数,其中 x = 0 x=0 x=0​​​代表 l l l​​​, x = r x=r x=r​​​代表 r r r​​​,如此, d p [ l ] [ r ] [ 0 ] 由 d p [ l + 1 ] [ r ] [ x ] dp[l][r][0]由dp[l + 1][r][x] dp[l][r][0]dp[l+1][r][x]​​转移, d p [ l ] [ r ] [ 1 ] 由 d p [ l ] [ r − 1 ] [ x ] dp[l][r][1]由dp[l][r - 1][x] dp[l][r][1]dp[l][r1][x]​​​转移而来,因为我们是参照某一处修改,那么这一处我们自然是不用变更颜色的,这里我们再以 d p [ l ] [ r ] [ 0 ] dp[l][r][0] dp[l][r][0]​​为例,如果是 d p [ l + 1 ] [ r ] [ 0 ] dp[l+1][r][0] dp[l+1][r][0]​​​​,说明此刻我们需要需要付出代价 1 1 1​​来使得 [ l + 1 , r ] [l+1,r] [l+1,r]​​区间的颜色变成 l l l​​​处的颜色,因为我们保证了每个连通分量都是一个点(所以这里我们先要预处理缩点,即是将连续值相同的区间变为一个点);而如果是 d p [ l + 1 ] [ r ] [ 1 ] dp[l+1][r][1] dp[l+1][r][1],那么我们需要判断 l l l r r r处是不是相等的,即是否需要代价来更改,同理对于另一状态处理方式相同。

    至此,按照区间 d p dp dp套路去写即可,注意预处理 d p dp dp的初值,即一开始默认最小操作次数为无穷大。

  • AC代码

/**
  *@filename:D
  *@author: pursuit
  *@created: 2021-08-16 15:48
**/
#include <bits/stdc++.h>
#define debug(a) cout << "debug : " << (#a)<< " = " << a << endl

using namespace std;

typedef pair<int,int> pii;
typedef long long ll;
const int N = 5e3 + 10;
const int P = 1e9 + 7;
const int INF = 0x3f3f3f3f;

int n,c[N];
int dp[N][N][2];
void solve(){
    //将连通分量缩点,因为已经颜色相同了,便于我们自身处理。
    int cnt = 0;
    int i = 1,j;
    while(i <= n){
        j = i;
        while(j + 1 <= n && c[j + 1] == c[i])j ++;
        c[++cnt] = c[j];
        //cout << c[cnt] << " ";
        i = j + 1;
    }
    n = cnt;
    //初始化。
    for(int i = 1; i <= n; ++ i){
        for(int j = i; j <= n; ++ j){
            dp[i][j][0] = dp[i][j][1] = i == j ? 0 : INF;
        }
    }
    //枚举区间长度。
    int r;
    for(int len = 2; len <= n; ++ len){
        //枚举左端点。
        for(int l = 1; l + len - 1 <= n; ++ l){
            r = l + len - 1;
            //此状态。
            dp[l][r][0] = min(dp[l][r][0],min(dp[l + 1][r][0] + 1, dp[l + 1][r][1] + (c[l] != c[r])));
            dp[l][r][1] = min(dp[l][r][1],min(dp[l][r - 1][0] + (c[l] != c[r]), dp[l][r - 1][1] + 1));
        }
    }
    printf("%d\n", min(dp[1][n][0],dp[1][n][1]));
}
int main(){	
    scanf("%d", &n);
    for(int i = 1; i <= n; ++ i){
        scanf("%d", &c[i]);
    }
    solve();
    return 0;
}

E. Arithmetic Progression

  • 解题思路

    由于操作 1 1 1是确定某一下标的值,操作 2 2 2是获取最大值,两个操作互不影响。对于我们来说,如果确定了最大值,即是确定了最后一项,那么我们肯定是要利用这个值的,所以我们可以二分查找确定最大值,由于 x ≤ 1 0 9 x\leq10^9 x109,所以我们二分查找次数不会超过 30 30 30,题目限制给出了 60 60 60的查询约束,所以我们还有 30 30 30次操作可以使用操作 1 1 1。试想,如果我们得到了 a i a_i ai,那么 m a x x − a i maxx-a_i maxxai即是 k d kd kd d d d即为公差,那么如果确定了 d d d a 1 = m a x x − ( n − 1 ) × d a_1=maxx-(n-1)\times d a1=maxx(n1)×d,所以我们的目标就是确定 d d d

    根据以上分析,每次查询都能得到 k d kd kd,所以如果我们查询的 30 30 30次得到的存在 k 1 d , k 2 d k_1d,k_2d k1d,k2d,其中 k 1 d k_1d k1d k 2 d k_2d k2d互质,那么 g c d ( k 1 d , k 2 d ) gcd(k_1d,k_2d) gcd(k1d,k2d)即是等于 d d d。而我们又知道 k ∈ [ 0 , n − 1 ] k\in[0,n-1] k[0,n1],所以我们只要取到其中互质的两个值即可,这怎么实现呢?我们可以利用随机数生成器mt19937生成 k k k,那么由于分布随机,在 30 30 30​次内能得到互质的两个数,至此,即可解决此题。

  • AC代码

/**
  *@filename:E
  *@author: pursuit
  *@created: 2021-08-16 16:59
**/
#include <bits/stdc++.h>
#define debug(a) cout << "debug : " << (#a)<< " = " << a << endl

using namespace std;

typedef pair<int,int> pii;
typedef long long ll;
const int N = 1e5 + 10;
const int P = 1e9 + 7;
const int INF = 0x3f3f3f3f;

/*
两种类型的查询。
第一种是给出下标i获取ai的值。
第二种是给出x返回是否存在严格大于x的元素,即判断x是不是最大值。
首先我们可以通过第二种操作二分找到最大值。
*/
int n;
map<int,bool> p;
int find(){
    int l = 0,r = 1e9,x;
    while(l < r){
        int mid = (l + r) >> 1;
        cout << "> " << mid << endl;
        cout.flush();
        cin >> x;
        if(x){
            l = mid + 1;
        }
        else{
            r = mid;
        }
    }
    return l;
}
void solve(){
    int maxx = find(),x,d = 0;
    //开始随机查询。
    mt19937 rnd(1e9 + 7);
    for(int i = 1; i <= min(30,n); ++ i){
        int idx;
        while(idx = rnd() % n + 1){
            if(p[idx])continue;
            p[idx] = 1;
            break;
        }
        cout << "? " << idx << endl;
        cin >> x;
        d = __gcd(d,maxx - x);
    }
    cout << "! " << maxx - (n - 1) * d << " " << d << endl;
}
int main(){	
    cin >> n;
    solve();
    return 0;
}
posted @ 2022-03-26 16:48  unique_pursuit  阅读(31)  评论(0)    收藏  举报