Codeforces Round #624 (Div. 3)

ぐ巨炮叔叔 提交于 2020-02-26 00:34:29

A.题意:通过加奇数减偶数的操作从a到b最少需要几步

签到题

#include <algorithm>
#include <iostream>
#include <cstring>
#include <cstdio>
#include <vector>
#include <cmath>
#include <queue>
#include <deque>
#include <map>
using namespace std;
typedef long long ll;
const double inf=1e20;
const int maxn=100005;
 
int main(){
    int n;
    scanf("%d",&n);
    for(int i=0;i<n;i++){
        ll a,b;
        scanf("%lld%lld",&a,&b);
        ll c=b-a;
        if(c==0){
            printf("0\n");
        }else if(c>0&&c%2==0){
            printf("2\n");
        }else if(c<0&&c%2!=0){
            printf("2\n");
        }
        else printf("1\n");
    }
 
    return 0;
}

 

B.题意:有一个数组p,你可以任意次交换a[pi]和a[pi+1],问能不能把数组变成一个非严格上升的子序列。

解:很明显,如果p数组中有连续的一段,那么这一段数字就是可以任意交换的,把连续的若干段都进行排序,就已经尽最大可能满足题目要求了,在与满足条件的数组比较一下即可。

#include <algorithm>
#include <iostream>
#include <cstring>
#include <cstdio>
#include <vector>
#include <cmath>
#include <queue>
#include <deque>
#include <map>
using namespace std;
typedef long long ll;
const double inf=1e20;
const int maxn=200+10;
 
void merge_sort(int *a,int x,int y,int *t){
    if(y-x>1){
        int m=x+(y-x)/2;
        int p=x,q=m,i=x;
        merge_sort(a,x,m,t);
        merge_sort(a,m,y,t);
        while(p<m||q<y){
            if(q>=y||(p<m&&a[p]<=a[q]))t[i++]=a[p++];
            else t[i++]=a[q++];
        }
        for(i=x;i<y;i++)a[i]=t[i];
    }
}
 
int a[maxn],b[maxn],c[maxn],d[maxn],e[maxn];
 
int main(){
    int t;
    scanf("%d",&t);
    while(t--){
        int n,m;
        scanf("%d%d",&n,&m);
        for(int i=0;i<n;i++){
            scanf("%d",&a[i]);
            d[i]=a[i];
        }
 
        for(int i=0;i<=n;i++)c[i]=0;
        for(int i=0;i<m;i++){
            scanf("%d",&b[i]);
            b[i]--;
            c[b[i]]=1;
        }
        c[n]=0;
        int l,r;
        l=r=-1;
        for(int i=0;i<=n;i++){
            if(c[i]==1){
                if(l==-1){
                    l=i;
                }
            }else{
                if(l!=-1){
                    merge_sort(a,l,i+1,e);
                    l=-1;
                }
            }
        }
        sort(d,d+n);
        int o=1;
        for(int i=0;i<n;i++){
            if(a[i]!=d[i])o=0;
        }
        if(o)printf("YES\n");
        else printf("NO\n");
    }
    return 0;
}

 

C.题意:求每个字母出现的次数

解:因为错误的顺序并不会影响结果,所以直接先处理短的,再处理长的。

#include <algorithm>
#include <iostream>
#include <cstring>
#include <cstdio>
#include <vector>
#include <cmath>
#include <queue>
#include <deque>
#include <map>
using namespace std;
typedef long long ll;
const double inf=1e20;
const int maxn=2*1e5+10;
 
int a[maxn];
char s[maxn];
int w[26],e[26];
int main(){
    int t;
    scanf("%d",&t);
    while(t--){
        int n,m;
        scanf("%d%d",&n,&m);
        getchar();
        scanf("%s",s);
        for(int i=0;i<m;i++){
            scanf("%d",&a[i]);
        }
        sort(a,a+m);
        for(int i=0;i<26;i++){
            w[i]=e[i]=0;
        }
        int j=0;
        for(int i=0;i<n;i++){
            w[s[i]-'a']++;
            while(a[j]-1==i&&j<m){
                    j++;
                for(int k=0;k<26;k++){
                    e[k]+=w[k];
                }
            }
        }
 
            for(int k=0;k<26;k++){
                e[k]+=w[k];
            }
 
        for(int i=0;i<26;i++){
            if(i<25)printf("%d ",e[i]);
            else printf("%d\n",e[i]);
        }
    }
    return 0;
}

 

D.题意:给a,b,c。每一步你可以让abc中任意一个加1或者减1.求让c是b的倍数且b是a的倍数最少需要多少步?

解:首先考虑这样一个问题,题目其他条件不变,只把c去掉。那么问题就变成了给a,b。每一步你可以让ab中任意一个加1或者减1.求让b是a的倍数最少需要多少步?

  两个做法:1,很明显,如果a确定了,b也就是确定的。那么就可以通过枚举a来确定b。时间复杂度为枚举a的时间复杂度。2,如果b确定,那么a就是b的因子中最接近原来的a的那个,说明a也是确定的。时间复杂度为枚举b的时间复杂度*求b因子的时间复杂度。

  回到原题:做法就比较显而易见了,两个做法:1.显然b是最关键的一个字。所以枚举b,a用b的因子确定,c可以直接确定。时间复杂度为枚举b的时间复杂度*求b因子的时间复杂度。2.枚举a,这时因为有c的存在,显然不能直接确定b是多少,所以枚举b为a的倍数,对于每个b都可以确定唯一一个c。时间复杂度为枚举a*枚举a的倍数O(nlogn)

  下代码为做法2,需要注意的是枚举b时需要比1e4大

#include <algorithm>
#include <iostream>
#include <cstring>
#include <cstdio>
#include <vector>
#include <cmath>
#include <queue>
#include <deque>
#include <map>
using namespace std;
typedef long long ll;
const double inf=1e20;
const int maxn=2*1e5+10;
 
int main(){
    int t;
    scanf("%d",&t);
    while(t--){
        int m=1e5;
        int a,b,c;
        int a1,b1,c1;
        scanf("%d%d%d",&a,&b,&c);
        for(int i=1;i<=1e4;i++){
            int num=0;
            for(int j=i;j<=2e4;j+=i){
                num=0;
                num+=max(i-a,a-i);
                num+=max(j-b,b-j);
 
                int k;
                if(c%j>j/2){
                    k=(c+j-1)/j*j;
                }else{
                    k=c/j*j;
                }
                if(k<j)k=j;
                num+=max(k-c,c-k);
                if(num<m){
                    m=num;
                    a1=i;
                    b1=j;
                    c1=k;
                }
            }
        }
        printf("%d\n",m);
        printf("%d %d %d\n",a1,b1,c1);
    }
    return 0;
}

 

E.题意,一个n节点的二叉树,能不能让所有节点的深度和为m,如果能,构造出这棵树。

解:1.很明显,如果一颗n节点树是一条链,那么此时的深度和为n节点树所能达到的上线。

  2.同理,如果一颗n节点的二叉树为完全二叉树,那么此时的深度和为n节点树所能达到的下线。

  3.如何证明下线到上线之间每个数字都是可取的呢,假设现在又n个节点形成一条链,深度和为m,如何构造一颗深度和为m-1的树,答:最深 的那个节点移动到上一层。如何构造一颗深度和为m-2的树?答:还是刚才那个节点再向上移动一层。如此反复,直到此节点不能向上移动,那么开始移动下个一最深的节点。每一步都使得深度和减一,并且由一条链开始,最所有节点无法移动时会变成一颗完全二叉树。

  4.如何模拟这个过程构造树。如果直接模拟这个过程是非常难模拟的,这道题的关键字是深度,所以我再这个模拟过程中去掉其他东西,只保留每个深度有多少节点,最后问题就变成了已知每个深度有多少节点,构造一颗二叉树。

  5.补充:代码中的b数组代表第i层最多容纳b[i]个节点,那么有一行为if(b[i]>10000)b[i]=10000;时什么意思,答,因为是二叉树,所以b每次*2,有5000个节点,也就是最深为5000层,早就爆int了,但同时发现,只有5000个节点,也就是说在这个做法中最多的时候一层只有2500个。所以当b>2500的时候,需要赋值为一个大于2500的数字就能保证准确性。

#include <algorithm>
#include <iostream>
#include <cstring>
#include <cstdio>
#include <vector>
#include <cmath>
#include <queue>
#include <deque>
#include <map>
using namespace std;
typedef long long ll;
const double inf=1e20;
const int maxn=2*1e5+10;
 
int a[maxn];
int b[maxn];
int c[maxn];
int main(){
    int t;
    scanf("%d",&t);
    while(t--){
        ll n,d;
        ll s,x;
        scanf("%lld%lld",&n,&d);
        s=(0+n-1)*n/2;
        x=0;
        ll nn=n;
        for(int i=1,j=0;nn>0;i*=2,j++){
            if(nn>=i){
                x+=i*j;
            }else{
                x+=nn*j;
            }
            nn-=i;
        }
        //printf("%lld %lld \n",x,s);
        if(x<=d&&d<=s){
            printf("YES\n");
 
            for(int i=0;i<n;i++){
                a[i]=1;
                if(i==0)b[i]=1;
                else b[i]=b[i-1]*2;
                if(b[i]>10000)b[i]=10000;
            }
            int j=n-1;
            int jj=n-1;
            int qwe=s-d;
 
            for(int i=0;i<qwe;i++){
                //printf("%d %d \n",a[j-1],b[j-1]);
                if(a[j-1]>=b[j-1]){
                    j=jj;
                    //printf(".....");
                }
                if(j==jj&&a[jj]==1){
                    jj--;
                }
 
                a[j]--;
                a[--j]++;
                /*
                for(int k=0;k<=jj;k++){
                    printf("%d ",a[k]);
                }printf("\n");
                */
            }
 
            int k=1;
            int kk=1;
            for(int i=1;i<=jj;i++){
                for(int j=0;j<a[i];j++){
                    k++;
                    c[k]=kk;
                    if(j%2==1)kk--;
                }
                kk=k;
            }
            for(int i=2;i<=n;i++){
                printf("%d ",c[i]);
            }
            printf("\n");
        }else printf("NO\n");
    }
    return 0;
}

 

F.

解:1.首先是最明显的一个条件,两个点要么会相遇,要么不会相遇,且不会相遇的两点在时间为0时最近。

  2.最简单的思路为枚举i枚举j时间复杂度为n2但显然超时,那么就只能线性枚举,当我们枚举到第i个的时候,前面的点有的在我们左边,有的在我们右边,有的往左走,有的往右走,有的比i点快,有的比i点满。何解?

    2.1首先发现,枚举i枚举j和ij的顺序无关,所以我们可以给数组按位置排序,排序后枚举到第i个点时,前面的点都在i的左侧。

    2.2说起运动,就有相对运动和绝对运动,题目中描述了一种相对坐标系的运动,如果给所有点都加上一个+1e9的速度,明显所有点的相对位置时不会变的,但是都变成了向右移动。这时发现相遇是否之和速度大小有关系,和值是多少关系不大,所以明显可以对速度离散化。

    2.3假设遍历到第i个点位置为x,速度为v,我们要得到是前i-1个点中不能与之相遇的点与i点的距离差的和。那么就需要知道这个样的点有多少个,假设为y,以及这些点到坐标原点0之间的距离和sum。那么前i-1个点中不能与之相遇的点与i点的距离差的和=x*y-sum

#include <algorithm>
#include <iostream>
#include <cstring>
#include <cstdio>
#include <vector>
#include <cmath>
#include <queue>
#include <deque>
#include <map>
using namespace std;
typedef long long ll;
const double inf=1e20;
const int maxn=2e5+10;
 
#define ls l,m,rt<<1;
#define rs m+1,,r,rt>>1|1;
 
ll n;
struct aa{
    ll a,v;
    aa(){}
    aa(ll a_,ll v_):a(a_),v(v_){}
}a[maxn];
 
bool cmp(aa a,aa b){
    return a.a<b.a;
}
 
ll m;
ll t[maxn];
void Init_hash(){
    for(int i=1;i<=n;i++){
        t[i]=a[i-1].v;
    }
    sort(t+1,t+1+n);
    m=unique(t+1,t+1+n)-t-1;
}
 
int hashh(int x){
    return lower_bound(t+1,t+1+m,x)-t;
}
 
ll sum[maxn<<2];
 
void pushup(int rt){
    sum[rt]=sum[rt<<1]+sum[rt<<1|1];
}
void build(ll l,ll r,ll rt){
    if(l==r){
        sum[rt]=0;
        return ;
    }
    ll m=(l+r)/2;
    build(l,m,rt<<1);
    build(m+1,r,rt<<1|1);
    pushup(rt);
}
void update(ll L,ll C,ll l,ll r,ll rt){
    if(l==r){
        sum[rt]+=C;
        return;
    }
    ll m=(l+r)>>1;
    if(L<=m)update(L,C,l,m,rt<<1);
    else    update(L,C,m+1,r,rt<<1|1);
    pushup(rt);
}
ll query(ll L,ll R,ll l,ll r,ll rt){
    if(L<=l&&r<=R)return sum[rt];
    ll m=(l+r)/2;
    ll ans=0;
    if(L<=m)ans+=query(L,R,l,m,rt<<1);
    if(R> m)ans+=query(L,R,m+1,r,rt<<1|1);
    return ans;
}
 
ll sum2[maxn<<2];
 
void pushup2(int rt){
    sum2[rt]=sum2[rt<<1]+sum2[rt<<1|1];
}
void build2(ll l,ll r,ll rt){
    if(l==r){
        sum2[rt]=0;
        return ;
    }
    ll m=(l+r)/2;
    build2(l,m,rt<<1);
    build2(m+1,r,rt<<1|1);
    pushup2(rt);
}
void update2(ll L,ll C,ll l,ll r,ll rt){
    if(l==r){
        sum2[rt]+=C;
        return;
    }
    ll m=(l+r)>>1;
    if(L<=m)update2(L,C,l,m,rt<<1);
    else    update2(L,C,m+1,r,rt<<1|1);
    pushup2(rt);
}
ll query2(ll L,ll R,ll l,ll r,ll rt){
    if(L<=l&&r<=R)return sum2[rt];
    ll m=(l+r)/2;
    ll ans=0;
    if(L<=m)ans+=query2(L,R,l,m,rt<<1);
    if(R> m)ans+=query2(L,R,m+1,r,rt<<1|1);
    return ans;
}
 
int main(){
    scanf("%lld",&n);
    for(int i=0;i<n;i++){
        scanf("%lld",&a[i].a);
    }
    for(int i=0;i<n;i++){
        scanf("%lld",&a[i].v);
    }
 
    Init_hash();
    sort(a,a+n,cmp);
 
    build(1,n,1);
    build2(1,n,1);
 
    ll num=0;
    for(int i=0;i<n;i++){
        ll vv=hashh(a[i].v);
 
        ll x=query(1,vv,1,n,1);
        ll summ=query2(1,vv,1,n,1);
        num+=(x*a[i].a-summ);
        //printf("%lld %lld...\n",x,summ);
 
        update(vv,1,1,n,1);
        update2(vv,a[i].a,1,n,1);
        //printf("%lld\n",num);
    }
    printf("%lld\n",num);
    return 0;
}

 

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!