1. 程式人生 > >CF每日一練(2.11)

CF每日一練(2.11)

只需要 nod tor part got include ons main 連續

CF-1114

A. Got Any Grapes?

skip

B. Yet Another Array Partitioning Task

  • 將n個數分成連續的k組,使得每組的前m大的數字的總和最大。
  • 首先可以想到肯定可以包含n個數中前 m*k 大的數。所以可以先將他們標記,然後掃一遍確定每組的端點即可
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
int n,m,k;
struct node{
    int x;
    int id;
}a[200010];
int v[200010];
bool cmp(node a,node b){
    return a.x>b.x;
}
int main(){
    cin>>n>>m>>k;
    for(int i=1;i<=n;i++){
        scanf("%d",&a[i].x);
        a[i].id = i;
    }
    sort(a+1,a+n+1,cmp);
    ll sum = 0;
    for(int i=1;i<=m*k;i++){
        v[a[i].id] = 1;
        sum += a[i].x;
    }
    vector<int> ans;
    int num = 0;
    for(int i=1;i<=n;i++){
        if(v[i])num++;
        if(num==m){
            ans.push_back(i);num=0;
            if(ans.size()==k-1)
                break;
        }
    }
    cout<<sum<<endl;
    for(int i=0;i<k-1;i++)
        cout<<ans[i]<<‘ ‘;
    puts("");
    return 0;
}

C. Trailing Loves (or L‘oeufs?)

  • $ n! = p_1^{x_1} \cdot p_2^{x_2}\cdots p_m^{x_m} \cdot Q$
  • \(b = p_1^{y_1} \cdot p_2^{y_2} \cdots p_m^{y_m}\)

分解n!的質因數復雜度為 O(log N)。所以我們可以將b分解質因數,對於質因數\(p_i\),計算n!含有多少個質因子\(p_i\) (設\(x_i\)) ,則該質因子下答案為 \(\lfloor x_i/y_i \rfloor\) , 最終\(ans = min \{ \lfloor x_1/y_1 \rfloor \cdots \lfloor x_m/y_m\rfloor \}\)

  • 計算n!中含有多少個\(p_i\) 時,可以這樣計算:
    • 首先在1到n的排列中肯定有\(\lfloor n/p_i \rfloor\)個包含質因子\(p_i\)的數,同理也有\(\lfloor x/{p_i^2}\rfloor\) 個含有兩個\(p_i\)的數,不過其中的一個質因子已經在\(\lfloor n/p_i \rfloor\)中統計過,所以只需要再統計第二個質因子,即累加上\(\lfloor x/{p_i^2}\rfloor\),而不是\(2*\lfloor x/{p_i^2}\rfloor\)
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const ll inf = LONG_LONG_MAX;
ll n,b;
ll calc(ll p,ll cnt){
    //res為上述xi
    ll res = 0,base = 1;
    for(;base<=n/p;){
        base*=p;
        res += n/base;
    }
    /*寫成下面這樣會爆
    for(base=x;base<=n;base*=x){
        res += n/base;
    }*/
    return res/cnt;
}
int main(){
    cin>>n>>b;
    ll ans = inf;
    //分解質因數
    for(ll i=2;i*i<=b;i++){
        if(b%i==0){
            ll cnt = 0;//cnt為上述yi
            while(b%i==0)b/=i,cnt++;
            ans = min(ans,calc(i,cnt));
        }
    }
    if(b>1) ans = min(ans,calc(b,1));
    cout<<ans<<endl;
}

D. Flood Fill

  • (又是一個沒見過的區間DP,題解裏面說可以倒過來LCS,相當於求最長回文子序列,不過我還沒搞懂
  • d[i][j][0]表示區間[i,j]所有數字與a[i]相同時所需要的最少改變次數,d[i][j][1]表示與a[j]相同。復雜度為\(O(n^2)\)
  • 每次轉移只能向左或者向右移動一格,細節看代碼吧
#include <bits/stdc++.h>
using namespace std;
const int inf = 0x3f3f3f3f;
int n;
int a[5050];
int dp[5050][5050][2];
int main(){
    cin>>n;
    for(int i=1;i<=n;i++)scanf("%d",&a[i]);
    if(n==1){
        puts("0");return 0;
    }
    memset(dp,0x3f,sizeof dp);
    for(int i=1;i<=n;i++)dp[i][i][0] = dp[i][i][1] = 0;
    for(int i=n;i>=1;i--){
        for(int j=i;j<=n;j++){
            for(int k=0;k<2;k++){
                //c為標準
                int c = k==0?a[i]:a[j];
                if(j<n)
                    dp[i][j+1][1] = min(dp[i][j+1][1],dp[i][j][k]+(a[j+1]==c?0:1));
                if(i>1)
                    dp[i-1][j][0] = min(dp[i-1][j][0],dp[i][j][k]+(a[i-1]==c?0:1));
            }
        }
    }
    cout<<min(dp[1][n][0],dp[1][n][1])<<endl;
}

CF每日一練(2.11)