CF 1430 (Div.2)

$\text{A}$

依次暴力枚举 $3$ ,$5$ 的窗户个数,最后看能不能被 $7$ 整除即可

$code$ :

#include<cstdio>
#include<cctype>

inline int read(){
    int r=0,f=0;
    char c;
    while(!isdigit(c=getchar()))f|=(c=='-');
    while(isdigit(c))r=(r<<1)+(r<<3)+(c^48),c=getchar();
    return f?-r:r;
}

inline bool check(int n){
    for(int i=0;i<=n;i+=5)
        if(!((n-i)%7))return true;
    return false;
}

inline void print(int n){
    for(int i=0;i<=n;i+=5)
        if(!((n-i)%7)){
            printf("%d %d",i/5,(n-i)/7);
            return;
        }
}

inline void work(){
    int n=read();
    for(int i=0;i<=n;i+=3)
        if(check(n-i)){
            printf("%d ",i/3);
            print(n-i);
            puts("");
            return;
        }
    puts("-1");
}

int main(){
    int t=read();
    while(t--)work();
    return 0;
}

 

$\text{B}$

排个序以后答案就是前 $k+1$ 大的和

$code$ :

#include<cstdio>
#include<cctype>
#include<algorithm>

using namespace std;

#define maxn 202202

inline int read(){
    int r=0,f=0;
    char c;
    while(!isdigit(c=getchar()))f|=(c=='-');
    while(isdigit(c))r=(r<<1)+(r<<3)+(c^48),c=getchar();
    return f?-r:r;
}

int n,k,a[maxn];

long long ans;

inline void work(){
    ans=0;
    n=read(),k=read();
    for(int i=1;i<=n;i++)a[i]=read();
    sort(a+1,a+1+n);
    for(int i=0;i<=k;i++)ans+=a[n-i];
    printf("%lld\n",ans);
}

int main(){
    int t=read();
    while(t--)work();
    return 0;
}

 

$\text{C}$

将 $n$ 与 $n-2$ 合并,造出一个 $n-1$ ,然后将两个 $n-1$ 合并为一个

然后就 $n-1$ 与 $n-3$ 合并,造出 $n-2$ ,再拿它与 $n-4$ 合并,一直搞下去就可以搞到 $2$ 了

然后要注意特判 $n=2$

$code$ :

#include<cstdio>
#include<cctype>

#define maxn 202202

inline int read(){
    int r=0,f=0;
    char c;
    while(!isdigit(c=getchar()))f|=(c=='-');
    while(isdigit(c))r=(r<<1)+(r<<3)+(c^48),c=getchar();
    return f?-r:r;
}

int n,a[maxn],ans[maxn][2];

inline void work(){
    n=read();
    if(n==2){
        puts("2\n1 2");
        return;
    }
    ans[1][0]=n-2,ans[1][1]=n;
    ans[2][0]=ans[2][1]=n-1;
    for(int i=n-1;i>=3;i--)ans[n-i+2][0]=i-2,ans[n-i+2][1]=i;
    printf("2\n");
    for(int i=1;i<n;i++)
        printf("%d %d\n",ans[i][0],ans[i][1]);
}

int main(){
    int t=read();
    while(t--)work();
    return 0;
}

 

$\text{D}$

把每一段相同的缩成一个( $e.g. \ \ \  1110011110->3241$ )

当删第 $i$ 号点的时候,分类讨论(其实可以写成一类):

  1. 若 $len_i = 1$ ( $len_i$ 即该段还剩下的大小)时,先往后找第一个 $j$ 满足 $len_j > 1$ ,然后让 $len_j--$ (即删掉这段中的一个),$i++$ ,若找不到满足条件的 $j$ ,则说明后面的段大小全都是 $1$ ,删了一个以后还会因为规则删掉一个,此时 $i+=2$
  2. 若 $len_i > 1$ 时,直接 $i++$ 即可

$code$ :

#include<cstdio>
#include<cctype>

#define maxn 202202

inline int read(){
    int r=0,f=0;
    char c;
    while(!isdigit(c=getchar()))f|=(c=='-');
    while(isdigit(c))r=(r<<1)+(r<<3)+(c^48),c=getchar();
    return f?-r:r;
}

int n,m,ans,len[maxn];

char s[maxn];

inline void work(){
    ans=0;
    n=read(),m=0;
    scanf("%s",s+1);
    len[++m]=1;
    for(int i=2;i<=n;i++){
        if(s[i]!=s[i-1])len[++m]=0;
        len[m]++;
    }
    int i=1,j=1;
    while(i<=m){
        ans++;
        while(j<i)j++;
        while(j<=m&&len[j]<2)j++;
        if(j>m)i+=2;
        else i++,len[j]--;
    }
    printf("%d\n",ans);
}

int main(){
    int t=read();
    while(t--)work();
    return 0;
}

 

$\text{E}$

首先有一个结论,对于相同的字母,若我们给他们编号,那么 $reverse$ 完后相对顺序肯定不会变(因为没必要交换两个相同的)

所以我们可以算出每个位置最后所对应的位置 $p_i$ ,又因为每次交换相邻两个,且要把 $p$ 变成 $1,2,3,...,n$ ,可以想到最小次数就是逆序对个数,求一下就好了

$code$ :

#include<cstdio>
#include<cctype>

#define maxn 202202

#define lowbit(k) (k&(-k))

inline int read(){
    int r=0,f=0;
    char c;
    while(!isdigit(c=getchar()))f|=(c=='-');
    while(isdigit(c))r=(r<<1)+(r<<3)+(c^48),c=getchar();
    return f?-r:r;
}

char s[maxn];

long long ans;

int n,s_w[33],p[maxn],c[maxn],w[33][maxn];

inline void add(int k){
    for(;k<=n;k+=lowbit(k))c[k]++;
}

inline int sum(int k){
    int s=0;
    for(;k;k-=lowbit(k))s+=c[k];
    return s;
}

int main(){
    n=read();
    scanf("%s",s+1);
    for(int i=1;i<=n;i++){
        int num=s[i]-'a'+1;
        w[num][++s_w[num]]=i;
    }
    for(int i=1;i<=26;i++)
        for(int j=1;j<=s_w[i];j++)
            p[w[i][j]]=n-w[i][s_w[i]-j+1]+1;
    for(int i=1;i<=n;i++){
        add(p[i]);
        ans+=i-sum(p[i]);
    }
    printf("%lld\n",ans);
    return 0;
}

 

$\text{F}$

好题, $DP$ 和贪心都可以, $DP$ 似乎是 $O(n^2)$ 甚至 $O(n^2logn)$ ,可以通过本题,不过较为复杂,这里考虑贪心。

很明显,最优的应该是能不换弹就不换,但是对于当前一波,后面的都会影响到这一波的决策,所以考虑从后往前先预处理一下 $need_i$ 表示原本弹夹里的子弹至少要多少才能打完第 $i-n$ 波

然后预处理完以后能不换就不换,每次弹夹里剩余的子弹经过一波以后就是 $bullet=k-(a_i-bullet)%k$ ,但要考虑一种特殊情况:当 $bullet > a_i $ 时, 直接 $bullet-=a_i$ 即可(不过总结起来也可以写成 $bullet=(k-(a_i-bullet)%k))%k$ ,这里利用了一下 C++ 取模的性质)

$code$ :

#include<cstdio>
#include<cctype>

#define maxn 2222

#define lowbit(k) (k&(-k))

inline int read(){
    int r=0,f=0;
    char c;
    while(!isdigit(c=getchar()))f|=(c=='-');
    while(isdigit(c))r=(r<<1)+(r<<3)+(c^48),c=getchar();
    return f?-r:r;
}

inline long long max(long long a,long long b){
    return a>b?a:b;
}

int n,k,l[maxn],r[maxn],a[maxn],need[maxn];

long long ans;

int main(){
    n=read(),k=read();
    for(int i=1;i<=n;i++)
        l[i]=read(),r[i]=read(),a[i]=read();
    for(int i=n;i>=1;i--){
        int ned=a[i];
        if(r[i]==l[i+1])ned+=need[i+1]; 
        if(ned>1ll*(r[i]-l[i]+1)*k)return puts("-1"),0;
        need[i]=max(ned-1ll*(r[i]-l[i])*k,0);
    }
    int had=0;
    for(int i=1;i<=n;i++){
        if(had<need[i]){
            ans+=had;
            had=k;
        }
        ans+=a[i];
        had=had<a[i]?k-(a[i]-had)%k:had-a[i];
    }
    printf("%lld\n",ans);
    return 0;
}

 

posted @ 2020-10-13 21:42  一叶知秋‘  阅读(149)  评论(0编辑  收藏  举报