Codeforces Round #587 (Div. 3)

A. Prefixes
题意:
你每次可以将某个位置上的 a 改变为 b 或者将 b 改变为 a,使得对于所有偶数位置 i,[1,i] 的 a 的数量 = b 的数量
思路:
连续两个一样的,就将后一个改变为和前一个不一样。

#include<bits/stdc++.h>
using namespace std;
#define LL long long
const int N=2e5+5;
int n,m,k;
char a[N],b[N];
int main(){
    cin>>n;
    scanf("%s",a+1);
    int ans=0;
    for(int i=2;i<=n;i+=2){
        if(a[i]==a[i-1]){
            ans++;
            if(a[i]=='a')
                a[i]='b';
            else
                a[i]='a';
        }
    }
    cout<<ans<<endl<<a+1;
    return 0;
}

B. Shooting
题意:
有 n 个物品,第 i 个物品有一个耐久度 a i a_{i} ai,损坏它的花费为 x* a i a_{i} ai+1,其中 x 为之前损坏的物品数量,问损坏所有的物品的最小花费是多少以及顺序。
思路:
很明显,先损坏耐久度大的,直接根据耐久度排序就行。

#include<bits/stdc++.h>
using namespace std;
#define LL long long
const int N=2e5+5;
int n,m,k;
#define P pair<int,int>
P p[N];
int main(){
    cin>>n;
    for(int i=1;i<=n;i++){
        scanf("%d",&p[i].first);
        p[i].first*=-1;
        p[i].second=i;
    }
    sort(p+1,p+1+n);
    LL ans=0;
    for(int i=1;i<=n;i++)
        ans+=-p[i].first*(i-1)+1;
    cout<<ans<<endl;
    for(int i=1;i<=n;i++)
        printf("%d ",p[i].second);
    return 0;
}

C. White Sheet
题意:
给你3个矩形A,B,C , 问 B ⋃ C B\bigcup C BC 是否完全覆盖A
思路:
这题可以直接一个一个if去判断(我不喜欢讨论就没用这种写法),观察发现可以从面积下手, ( B ⋂ A ) ⋃ ( C ⋂ A ) = = A (B\bigcap A) \bigcup(C\bigcap A)==A (BA)(CA)==A 的话,则完全覆盖

S ( B ⋂ A ) ⋃ ( C ⋂ A ) = S ( B ⋂ A ) + S ( C ⋂ A ) − S ( ( B ⋂ A ) ) ⋂ ( C ⋂ A ) S_{(B\bigcap A) \bigcup(C\bigcap A)}= S_{(B\bigcap A) } + S_{(C\bigcap A)} - S_{((B\bigcap A))\bigcap (C\bigcap A)} S(BA)(CA)=S(BA)+S(CA)S((BA))(CA)

两个矩形的交的话很好求。左下角取大,右上角取小就行。

#include<bits/stdc++.h>
using namespace std;
#define LL long long
#define P pair<int,int>
const int N=2e8+5;
const LL INF=1e9;
const LL III=1e18;
int n,m;
int x[100],y[100];
void slove(int id1,int id2,int id3){
    if(x[id1]>=x[id2+1]||y[id1]>=y[id2+1])
        return ;
    swap(id1,id2);
    if(x[id1]>=x[id2+1]||y[id1]>=y[id2+1])
        return ;
    x[id3]=max(x[id1],x[id2]);
    y[id3]=max(y[id1],y[id2]);
    x[id3+1]=min(x[id1+1],x[id2+1]);
    y[id3+1]=min(y[id1+1],y[id2+1]);
}
LL mj(int id){
    return 1LL*(x[id+1]-x[id])*(y[id+1]-y[id]);
}
int main(){
 
    for(int i=1;i<=6;i++)
        scanf("%d%d",&x[i],&y[i]);
    slove(1,3,7);
    slove(1,5,9);
    slove(7,9,11);
    if((mj(7)+mj(9)-mj(11))!=mj(1))
        puts("YES");
    else
        puts("NO");
 
    return 0;
}

D. Swords
题意:
地宫开始有 n 总类型的剑,每种类型的剑有 x 个,有 y 个人冲进地宫,每个人只拿一种类型的剑并且拿 z 吧。第二天守卫来检查发现第 i 种类型的剑只剩下 a i a_{i} ai 把,先给你 n 和 a,试确定最小的 y 并且确定 z 。
思路:
玄学,直接感觉 x = m a x ( a i ) , i ∈ [ 1 , n ] x=max(a_{i}),i\in [1,n] x=max(ai),i[1,n],然后直接求就行.

#include<bits/stdc++.h>
using namespace std;
#define LL long long
const int N=2e5+5;
int n,m,k;
#define P pair<int,int>
int a[N];
int main(){
    cin>>n;
    int ma=0;
    for(int i=1;i<=n;i++){
        scanf("%d",&a[i]);
        ma=max(a[i],ma);
    }
    LL y;
    int z=0;
    for(int i=1;i<=n;i++)
        z=__gcd(z,ma-a[i]);
    y=0;
    for(int i=1;i<=n;i++)
        if(ma!=a[i])
            y+=(ma-a[i])/z;
    printf("%lld %d\n",y,z);
    return 0;
}

E2. Numerical Sequence (hard version)
题意:
给你个序列 11212312341234512345612345671234567812345678912345678910 ,问第k位。
分析:
考虑两个问题:
问题1: 1121231234…123…x 的长度是多少。( x ∈ [ 1 , 1 e 18 ] x\in [1,1e18] x[1,1e18]
我们可以通过每个数字的次数轻松算出来(请注意,数字不是数位)。
设 len 为 x 的值的长度。
F(1) = x

F(9) = x-8
F(10) = x-9

F(99) = x-98

F( 1 0 l e n − 1 10^{len-1} 10len1) = x - 1 0 l e n − 1 10^{len-1} 10len1 +2
F(x) = 1
则规律以及出现,
长度为1的为 1-9 出现了 (x) + (x-1) + … + (x-8) 次
长度为2的为10-99 出现了 (x-9) + (x-10) + … + (x-98) 次

长度为len的为 1 0 l e n − 1 − x 10^{len-1}-x 10len1x 出现了 ( x − 1 0 l e n − 1 ) + . . . + 1 (x-10^{len-1}) + ... + 1 (x10len1)+...+1
然后直接等差数列求和就行了,或者预处理好。
知道这个问题我门就可以二分,确定结尾那一块。
现在我们还需要得到他具体属于哪个数字。

问题2:一个序列1234…x的长度
这个也可以通过二分实现,通过枚举长度来check。最后就知道答案在那个数字里,以及在哪一位,这个很好写。

#include<bits/stdc++.h>
using namespace std;
#define LL long long
#define P pair<int,int>
const int N=2e8+5;
const LL INF=1e9;
int n,m;
LL k;
LL fi[100];
int str(LL x){
    if(x==0)return 0;
    int len=0;
    while(x)
        len++,x/=10;
    return len;
}
LL check(LL x){
   if(x==0)return 0;
    int len=str(x);
    LL sum=0;
    for(int i=1;i<len;i++)
        sum+=(x-fi[i-1]+1 + x - fi[i] +2)*(fi[i]-fi[i-1])/2*i;
    sum+=(x - fi[len-1] + 1 +1)*(x - fi[len-1] + 1)/2*len;
    return sum;
}
LL check2(LL x){
    if(x==0)return 0;
    int len=str(x);
    LL sum=0;
    for(int i=1;i<len;i++)
        sum+=9*fi[i-1]*i;
    sum+=(x-fi[len-1]+1)*len;
    return sum;
}
int main(){

    fi[0]=1;
    for(int i=1;i<=15;i++)
        fi[i]=fi[i-1]*10;
    int q;
    cin>>q;
    while(q--){
        scanf("%lld",&k);
        //问题1
        LL l=1,r=INF;
        LL ans;
        while(l<=r){
            LL mid=(l+r)>>1;
            LL x=check(mid);
            if(x<k)
                l=mid+1;
            else {
                ans=mid;
                r=mid-1;
            }
        }

        //问题2
        k-=check(ans-1);
        l=1,r=ans;
        while(l<=r){
            LL mid=(l+r)>>1;
            LL x=check2(mid);
            if(x<k)
                l=mid+1;
            else {
                ans=mid;
                r=mid-1;
            }
        }
        k-=check2(ans-1);
        char ch[39];
        sprintf(ch+1,"%lld",ans);
        printf("%c\n",ch[k]);
    }
    return 0;
}

F:DP太难了。

#include<bits/stdc++.h>
using namespace std;
#define LL long long
const int N=2e5+5;
char a[N];
multiset<LL>val,mu;
vector<LL>del[N];
LL dp[N];
int main(){
    int n,k;
    cin>>n>>k;
    scanf("%s",a+1);
    dp[n+1]=0;
    mu.insert(0);
    for(int i=n;i>=1;i--) {
       if(i+k+2<=n+1)       //删除无用的
            mu.erase(mu.find(dp[i+k+2]));
        for(auto it:del[i])
            val.erase(val.find(it));

        dp[i]=dp[i+1]+i;
        if(!val.empty())
            dp[i]=min(dp[i],*val.begin());
        if(a[i]=='1'){
            LL x=*mu.begin()+i;
            dp[i]=min(dp[i],x);
            val.insert(x);
            if(i-k-1>=1)
                del[i-k-1].push_back(x);
        }
        mu.insert(dp[i]);
    }
    printf("%lld\n",dp[1]);
    return 0;
}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值