zoukankan      html  css  js  c++  java
  • Educational Codeforces Round 23 补题小结

    昨晚听说有教做人场,去补了下玩。

    大概我的水平能做个5/6的样子?

    (不会二进制Trie啊,我真菜)

    A.

    傻逼题。大概可以看成向量加法,判断下就好了。

    #include<iostream>
    #include<cstdio>
    #include<algorithm>
    #include<cmath>
    using namespace std;
    int x1,x2,yy1,y2,x,y;
    int main(){
        scanf("%d%d%d%d%d%d",&x1,&yy1,&x2,&y2,&x,&y);
        int dx=abs(x1-x2),dy=abs(yy1-y2);
        if((abs(dx/x-dy/y)%2==0)&&(dx%x==0)&&(dy%y==0))puts("YES");
        else puts("NO");
    }

    B.

    找符合要求的最小三元组乘积出现次数。

    sort一下随便搞搞就行了。

    #include<bits/stdc++.h>
    #define N 100010
    typedef long long ll;
    using namespace std;
    ll a[N];int n;ll minv;
    inline ll read(){
        ll f=1,x=0;char ch;
        do{ch=getchar();if(ch=='-')f=-1;}while(ch<'0'||ch>'9');
        do{x=x*10+ch-'0';ch=getchar();}while(ch>='0'&&ch<='9');
        return f*x;
    }
    int main(){
        n=read();
        for(int i=1;i<=n;i++)a[i]=read();
        sort(a+1,a+n+1);ll cnt=0;
        for(int i=1;i<=n;i++)if(a[i]==a[3])cnt++;
        if(a[1]==a[3])cout<<(cnt-2)*(cnt-1)*cnt/6<<endl;
        else if(a[2]==a[3])cout<<(cnt-1)*cnt/2<<endl;
        else cout<<cnt<<endl;
    }

    C.

    第一反应数位dp,反正也可做。

    数位记忆化搜索大概也行的样子。

    但是可以显然地证明一个性质:x + 1 - sumd(x + 1) ≥ x - sumd(x)

    这就满足了一个单调性,按照出题人的想法是可以二分答案。

    但是可以直接枚举最大的范围嘛!干嘛非要写个二分答案

    而且这么写跑得飞快。

    #include<bits/stdc++.h>
    using namespace std;
    typedef long long ll;
    ll n,s;
    inline bool check(ll x){
        ll res=x;
        while(res){x-=res%10;res/=10;}
        return x>=s;
    }
    int main(){
        cin>>n>>s;ll maxv=min(n,s+180),sum=0;
        for(ll i=s;i<=maxv;i++)if(check(i))++sum;
        cout<<n-maxv+sum<<endl;
    }

    D.

    用单调栈维护一个"一个数向左向右最大可以做max与min能管的距离"

    好久不上语文课了表达能力=0

    实在不行看官方题解吧。

    #include<bits/stdc++.h>
    #define N 1000005
    #define inf 1000000007
    typedef long long ll;
    using namespace std;
    int n,a[N];
    ll ans1,ans2,ans;
    stack<int>s1,s2;
    inline int read(){
        int f=1,x=0;char ch;
        do{ch=getchar();if(ch=='-')f=-1;}while(ch<'0'||ch>'9');
        do{x=x*10+ch-'0';ch=getchar();}while(ch>='0'&&ch<='9');
        return f*x;
    }
    int main(){
        n=read();
        s1.push(0);s2.push(0);
        for(int i=1;i<=n;i++){
            a[i]=read();a[0]=0;
            while(s1.top()&&a[i]<a[s1.top()]){
                int top=s1.top();s1.pop();
                ans1-=1LL*a[top]*(top-s1.top());
            }
            ans1+=1LL*a[i]*(i-s1.top());s1.push(i);
            a[0]=inf;
            while(s2.top()&&a[i]>a[s2.top()]){
                int top=s2.top();s2.pop();
                ans2-=1LL*a[top]*(top-s2.top());
            }
            ans2+=1LL*a[i]*(i-s2.top());s2.push(i);
            ans+=ans2-ans1;
        }
        cout<<ans<<endl;
    }


    E.

    我太菜了,以前居然没见过这种在二进制字典树上的贪心……

    建一个二进制的字典树,然后贪心一下看能不能搞成1就行了。

    具体的我日后可能得写个blog介绍下Trie的贪心。

    #include<bits/stdc++.h>
    #define N 3000005
    using namespace std;
    int size[N],ch[N][2],n,cnt,a,b,type;
    inline void ins(int x,int add){
        int now=1;
        for(int i=26;i>=0;i--){
            bool v=((x>>i)&1);
            if(!ch[now][v])ch[now][v]=++cnt;
            now=ch[now][v];
            size[now]+=add;
        }
    }
    inline void query(int x,int y){
        int ans=0,now=1,val=0;
        for(int i=26;i>=0&&now;i--){
            bool xbit=((x>>i)&1),ybit=((y>>i)&1);
            val+=val;
            if(ybit){
                ans+=size[ch[now][xbit]];now=ch[now][!xbit];
                val+=!xbit;
            }
            else{now=ch[now][xbit];val+=xbit;}
        }
        printf("%d
    ",ans);
    }
    inline int read(){
        int f=1,x=0;char ch;
        do{ch=getchar();if(ch=='-')f=-1;}while(ch<'0'||ch>'9');
        do{x=x*10+ch-'0';ch=getchar();}while(ch>='0'&&ch<='9');
        return f*x;
    }
    int main(){
        cnt=1;n=read();
        while(n--){
            int opt=read(),x=read();
            if(opt==1)ins(x,1);
            if(opt==2)ins(x,-1);
            if(opt==3){
                int y=read();
                query(x,y);
            }
        }
    }

    F.

    看到mex突然激动,感觉可能是个DS题。

    果然是。

    第一反应主席树求一下就好,后来想下,主席树也是没有必要的。

    直接线段树维护就行。

    类似于取反,求最左节点的操作。

    值域过大可以选择动态开点或者离散化。

    我就写了个离散化……

    类似的分解的操作的题bzoj都有,如果一下子看不懂这篇

    可以QQ找我问几个原题。

    #include<bits/stdc++.h>
    #define N 300005
    #define lson (o<<1)
    #define rson (o<<1|1)
    using namespace std;
    typedef long long ll;
    ll a[N];
    struct Query{ll l,r,opt;}Q[N];
    int n,len;
    struct Segment_Tree{
        int sumv[N<<4],addv[N<<4],rev[N<<4];
        inline void pushup(int o){sumv[o]=sumv[lson]+sumv[rson];}
        inline void puttag(int o,int l,int r,int add,int re){
            if(re){
                if(addv[o]==-1)rev[o]^=1,sumv[o]=r-l+1-sumv[o];
                else addv[o]^=1,sumv[o]=r-l+1-sumv[o];
            }
            else if(add!=-1){
                rev[o]=0;addv[o]=add;sumv[o]=(r-l+1)*add;
            }
        }
        inline void pushdown(int o,int l,int r){
            int mid=(l+r)>>1;
            puttag(lson,l,mid,addv[o],rev[o]);
            puttag(rson,mid+1,r,addv[o],rev[o]);
            addv[o]=-1;rev[o]=0;
        }
        void build(int o,int l,int r){
            if(l==r){addv[o]=-1;return;}
            int mid=(l+r)>>1;
            build(lson,l,mid);build(rson,mid+1,r);
            addv[o]=-1;
        }
        int querymex(int o,int l,int r){
            if(l==r)return l;
            int mid=(l+r)>>1;pushdown(o,l,r);
            if(sumv[lson]<mid-l+1)return querymex(lson,l,mid);
            else return querymex(rson,mid+1,r);
        }
        void change(int o,int l,int r,int ql,int qr,int add,int re){;
            if(ql<=l&&r<=qr){puttag(o,l,r,add,re);return;}
            int mid=(l+r)>>1;pushdown(o,l,r);
            if(ql<=mid)change(lson,l,mid,ql,qr,add,re);
            if(qr>mid)change(rson,mid+1,r,ql,qr,add,re);
            pushup(o);
        }
    }T;
    inline ll read(){
        ll f=1,x=0;char ch;
        do{ch=getchar();if(ch=='-')f=-1;}while(ch<'0'||ch>'9');
        do{x=x*10+ch-'0';ch=getchar();}while(ch>='0'&&ch<='9');
        return f*x;
    }
    int main(){
        n=read();
        for(int i=1;i<=n;i++){
            Q[i].opt=read();Q[i].l=read();Q[i].r=read();
        }
        a[1]=1;
        for(int i=1;i<=n;i++)a[3*i-1]=Q[i].l,a[3*i]=Q[i].r,a[3*i+1]=Q[i].r+1;
        sort(a+1,a+n*3+2);
        len=1;
        for(int i=2;i<=3*n+1;++i)if(a[i]!=a[i-1]) a[++len]=a[i];
        T.build(1,1,len);
        for(int i=1;i<=n;i++){
            int x=lower_bound(a+1,a+len+1,Q[i].l)-a,y=lower_bound(a+1,a+len+1,Q[i].r)-a;
            if(Q[i].opt==1)T.change(1,1,len,x,y,1,0);
            if(Q[i].opt==2)T.change(1,1,len,x,y,0,0);
            if(Q[i].opt==3)T.change(1,1,len,x,y,-1,1);
            printf("%lld
    ",a[T.querymex(1,1,len)]);
        }
    }

    啊最后总结下吧。

    姿势水平还不够,还得学习一个。

    数据结构要学会活学活用,用现有的水平解决一些不是很常规的问题。

    dp什么的思维还是不够,要学习一个。

    不过这场的外国人居然出了两个DS?

    但是可能外国的DS水平不如我国?这个Trie题讲道理可能在国内只有T2的难度吧。

    最后一个Segment-Tree可以算奇奇怪怪的常规应用的组合。

    所以还是要熟练呀~

  • 相关阅读:
    推箱子
    去掉两个最高分、去掉两个最低分,求平均分
    投票选班长
    彩票
    闰年、平年
    闹钟
    手机号抽奖
    for练习--侦察兵
    兔子、棋盘放粮食、猴子吃桃
    for练习--凑法
  • 原文地址:https://www.cnblogs.com/zcysky/p/7028707.html
Copyright © 2011-2022 走看看