• 2020 CCPC秦皇岛 正式赛题解


    A Greeting from Qinhuangdao 概率

    题意:$r$个红球,$b$个蓝球,不放回摸两次均为红球的概率。

    思路:基础概率论,注意一下概率为0的情况和分子分母的约分。

    代码:

     1 #include <bits/stdc++.h>
     2 
     3 #define debug(...)  fprintf(stderr,__VA_ARGS__)
     4 #define DEBUG printf("Passing [%s] in LINE %d
    ",__FUNCTION__,__LINE__)
     5 #define Debug debug("Passing [%s] in LINE %d
    ",__FUNCTION__,__LINE__)
     6 #define rep(i,x,y) for(int i=(x);i<=(y);++i)
     7 #define _max(a,b) (a)>(b)? (a):(b)
     8 #define _min(a,b) (a)<(b)? (a):(b)
     9 
    10 using namespace std;
    11 typedef long long ll;
    12 typedef unsigned long long ull;
    13 typedef pair<int,int> pii;
    14 typedef pair<ll,int> pli;
    15 typedef pair<int,ll> pil;
    16 typedef pair<ll,ll> pll;
    17 
    18 int t;
    19 int r,b;
    20 int main(){
    21     cin>>t;
    22     for(int cs=1;cs<=t;++cs){
    23         cin>>r>>b;
    24         if(r<=1){
    25             printf("Case #%d: 0/1
    ",cs);
    26             continue;
    27         }else{
    28             int fz=r*(r-1),fm=(r+b)*(r+b-1),re=__gcd(fz,fm);
    29             printf("Case #%d: %d/%d
    ",cs,fz/re,fm/re);
    30         }
    31     }
    32     return 0;
    33 }
    View Code

     比赛的时候顺利签到1A,虽然我敲得还是慢了一点


    C Cameraman

    题意:一个$W imes H$的矩形房间,给定Bob位置$(x,y)$,以及$m$个障碍物的坐标$(x_i,y_i)$,需要Alex站在一个点拍照,拍照的范围是Alex站的位置为顶点的一个角(可以大于180°),要求拍照范围内必须拍到Bob而不能拍到其他障碍物,求能拍到的墙壁的最大长度

    思路:这题是个假题,当时我们队的思路是Alex站在Bob的位置拍摄,以Bob为顶点,对其他障碍物进行一个极角排序,然后拍摄角度两条边卡在两个相邻障碍物上,求投影长度来更新答案。可以以Bob为原点建系,假设建系后的房屋左下坐标为$(cx,cy)$,右上坐标为$(dx,dy)$,维护一个从$(dx,0)$到某个障碍物扫过的墙壁的前缀和,然后求解两个障碍物之间的扫过的距离就前缀和来$O(1)$求解。根据边落在的象限情况分成8个情况,$(dx,0) arr(dx,dy) arr(0,dy) arr(cx,dy) arr(cx,0) arr(cx,cy) arr(0,cy) arr(dx,cy) arr(dx,0)$,进行简单的三角函数运算,特判一下最后一个跟第一个所围成的长度

    代码:(虽然是假算法,但是还是熟练一下极角排序)

    #include <bits/stdc++.h>
    
    #define debug(...)  fprintf(stderr,__VA_ARGS__)
    #define DEBUG printf("Passing [%s] in LINE %d
    ",__FUNCTION__,__LINE__)
    #define Debug debug("Passing [%s] in LINE %d
    ",__FUNCTION__,__LINE__)
    #define rep(i,x,y) for(int i=(x);i<=(y);++i)
    #define _max(a,b) (a)>(b)? (a):(b)
    #define _min(a,b) (a)<(b)? (a):(b)
    
    using namespace std;
    typedef long long ll;
    typedef unsigned long long ull;
    typedef pair<int,int> pii;
    typedef pair<ll,int> pli;
    typedef pair<int,ll> pil;
    typedef pair<ll,ll> pll;
    
    const double eps=1e-8;
    const double pi=acos(-1.0);
    const int MAXN=1e5+10;
    
    double w,h,bx,by;
    int n;
    double tta[MAXN];
    double dx,dy,cx,cy;
    
    int sgn(double x){
        if(fabs(x)<eps)     return 0;
        if(x>0) return 1;
        else return -1;
    }
    
    double get_angle(double x,double y){
        double ang=atan2(y,x);
        if(sgn(ang)<0)  ang=2*pi+ang;
        return ang;
    }
    
    inline double sqr(double x){
        return x*x;
    }
    
    struct Point{
        double x,y;
        Point(){}
        Point(double _x,double _y):x(_x),y(_y){}
        void input(){
            scanf("%lf%lf",&x,&y);
        }
        bool operator ==(const Point& b)const{
            return sgn(x-b.x)==0&&sgn(y-b.y)==0;
        }
        //Cross Product
        double operator ^(const Point& b)const{
            return x*b.y-y*b.x;
        }
        //Dot Product
        double operator *(const Point& b)const{
            return x*b.x+y*b.y;
        }
        double distance(const Point& b){
            return hypot(x-b.x,y-b.y);
        }
    }pt[MAXN];
    
    struct Line{
        Point s,e;
        Line(){}
        Line(Point _s,Point _e):s(_s),e(_e){}
        bool operator ==(const Line& b)const{
            return (s==b.s)&&(e==b.e);
        }
    };
    
    double getlen(double dd){
        double res=0;
        if(dd<=get_angle(dx,dy)){
            return res+dx*tan(dd);
        }else res+=dy;
        if(dd<=pi/2){
            return res+dx-dy*tan(pi/2-dd);
        }else res+=dx;
        if(dd<=get_angle(cx,dy)){
            return res+dy*tan(dd-pi/2);
        }else res-=cx;
        if(dd<=pi){
            return res+dy+cx*tan(pi-dd);
        }else res+=dy;
        if(dd<=get_angle(cx,cy)){
            return res-cx*tan(dd-pi);
        }else res-=cy;
        if(dd<=pi*3/2){
            return res-cx+cy*tan(3*pi/2-dd);
        }else res-=cx;
        if(dd<=get_angle(dx,cy)){
            return res-cy*tan(dd-3*pi/2);
        }else res+=dx;
        return res-cy-dx*tan(2*pi-dd);
    }
    
    
    void solve(int cs){
        scanf("%lf%lf%lf%lf",&w,&h,&bx,&by);
        scanf("%d",&n);
        dx=w-bx;
        dy=h-by;
        cx=0-bx;
        cy=0-by;
        rep(i,1,n){
            pt[i].input();
            tta[i]=getlen(get_angle(pt[i].x-bx,pt[i].y-by));
        }
        sort(tta+1,tta+1+n);
        double ans=0;
        rep(i,1,n-1)    ans=max(ans,tta[i+1]-tta[i]);
        ans=max(ans,2*(w+h)-(tta[n]-tta[1]));
        printf("Case #%d: %.8lf
    ",cs,ans);
    }
    
    int main(){
        int T;
        cin>>T;
        for(int cs=1;cs<=T;++cs){
            solve(cs);
        }
        return 0;
    }
    View Code

    比赛的时候最后1个多小时就在rush这道题,由于对板子还不是很熟,并且使用了复杂的处理方法,没能写完。但是赛后NB群友提出了在Bob在障碍物凸包外的话,这样的做法就假了,甚至连样例都过不去。


    E Exam Results 二分+树状数组

    题意:$n$个学生,及格率$p$,每个学生有可能获得两个分数:最高分$a_{i}$和最低分$b_{i}$,及格线为$n$个学生中的最高分*$p%$,问最好的情况下有多少的人能及格

    思路:对所有分数进行一个展开排序,枚举最高分,得到及格线,然后二分查找到及格线的位置,查询及格线到当前枚举的最高分的区间,查询区间内有多少个学生。可以使用树状数组维护区间里的种类数。维护手法:在第一次出现某个种类时,在相应的树状数组的位置进行+1,之后出现某个种类时,在树状数组的$last[i]$处$-1$,然后查询就通过树状数组进行区间相减得到种类。需要注意的是有可能从最低的分数开始枚举的最高分可能无效,因为可能当前的最高分不能包括所有$n$个人,所以可以从所有人的最低分的最高分开始枚举,也可以前缀和维护当前分数能包括多少人(P.S:树状数组能维护是因为这题的查询算是已经离线过的,对于其他无序的提问,就得对询问进行离线处理,例题:luogu P1972 [SDOI2009]HH的项链

    代码:

    #include <bits/stdc++.h>
    
    #define debug(...)  fprintf(stderr,__VA_ARGS__)
    #define DEBUG printf("Passing [%s] in LINE %d
    ",__FUNCTION__,__LINE__)
    #define Debug debug("Passing [%s] in LINE %d
    ",__FUNCTION__,__LINE__)
    #define rep(i,x,y) for(int i=(x);i<=(y);++i)
    #define _max(a,b) (a)>(b)? (a):(b)
    #define _min(a,b) (a)<(b)? (a):(b)
    
    using namespace std;
    typedef long long ll;
    typedef unsigned long long ull;
    typedef pair<int,int> pii;
    typedef pair<ll,int> pli;
    typedef pair<int,ll> pil;
    typedef pair<ll,ll> pll;
    
    int t;
    int n,P;
    const int MAXN=2e5+10;
    struct p{
        int val,id;
        bool operator <(const p& rh)const{
            return val==rh.val? id<rh.id:val<rh.val;
        }
    }pt[MAXN<<1];
    int lst[MAXN<<1],pr[MAXN<<1],ls[MAXN<<1],vs[MAXN];
    ll tr[MAXN<<1];
    
    void init(){
        rep(i,0,n)   lst[i]=0;
        rep(i,0,n)  vs[i]=0;
        rep(i,0,n<<1)   tr[i]=0;
    }
    
    int lowbit(int x){return x&-x;}
    
    void upd(int x,int k){
        for(;x<=2*n;x+=lowbit(x)) tr[x]+=k*1ll;
    }
    
    ll qr(int x){
        ll ret=0;
        for(;x;x-=lowbit(x)){
            ret+=tr[x];
        }
        return ret;
    }
    
    int main(){
        cin>>t;
        for(int cs=1;cs<=t;++cs){
            cin>>n>>P;
            init();
            double rat=P*0.01;
            int x,y;
            rep(i,1,n){
                scanf("%d%d",&x,&y);
                pt[i].id=pt[i+n].id=i;
                pt[i].val=x;
                pt[i+n].val=y;
            }
            sort(pt+1,pt+1+2*n);
            rep(i,1,n<<1) ls[i]=pt[i].val;
            rep(i,1,n<<1){
                if(!vs[pt[i].id]){
                    vs[pt[i].id]=1;
                    pr[i]=pr[i-1]+1;
                }else{
                    pr[i]=pr[i-1];
                }
            }
            ll ans=0;
            rep(i,1,n<<1){
                if(lst[pt[i].id]){
                    upd(lst[pt[i].id],-1);
                }
                upd(i,1);
                lst[pt[i].id]=i;
                if(pr[i]<n) continue;
                int res=ceil(pt[i].val*rat);
                int pos=lower_bound(ls+1,ls+2*n+1,res)-ls;
                ans=max(ans,qr(i)-qr(pos-1));
            }
            printf("Case #%d: %lld
    ",cs,ans);
        }
        return 0;
    }
    View Code

     比赛的时候队友Y想到了展开排序,然后我想到了枚举+二分,在思考维护区间种类的时候,尽管做过树状数组维护区间的题目,但是还是搞出了一个错的维护方法,幸亏队友H数据结构功力深厚,及时写出了正解,成功1A


    F Friendly Group 贪心

    题意:$n$个人,$m$组朋友关系,选择一些人${i}$,定义快乐值$p$为 $选择的人的集合之间的朋友关系数量-选择的人数$

    思路:可以发现,如果选择了一个人,那么选择所有他的朋友并不会使得快乐值更劣,因为$p_{next}=p_{current}-select_{next}+e_{next} geq p_{current}$,所以我们对于一个联通子图,我们要么全选,要么全不选。DFS更新答案即可。

    代码:

    #include <bits/stdc++.h>
    
    #define debug(...)  fprintf(stderr,__VA_ARGS__)
    #define DEBUG printf("Passing [%s] in LINE %d
    ",__FUNCTION__,__LINE__)
    #define Debug debug("Passing [%s] in LINE %d
    ",__FUNCTION__,__LINE__)
    #define rep(i,x,y) for(int i=(x);i<=(y);++i)
    #define _max(a,b) (a)>(b)? (a):(b)
    #define _min(a,b) (a)<(b)? (a):(b)
    
    using namespace std;
    typedef long long ll;
    typedef unsigned long long ull;
    typedef pair<int,int> pii;
    typedef pair<ll,int> pli;
    typedef pair<int,ll> pil;
    typedef pair<ll,ll> pll;
    
    const int MAXN=3e5+10;
    
    int t,n,m;
    vector<int> g[MAXN];
    int vis[MAXN],in[MAXN];
    ll ans=0;
    
    void init(){
        ans=0;
        for(int i=0;i<=n;++i)   g[i].clear();
        rep(i,0,n)  vis[i]=0,in[i]=0;
    }
    
    void dfs(int u,ll& cnt,int& tot){
        vis[u]=1;
        cnt+=in[u];
        tot++;
        for(int x:g[u]){
            if(!vis[x]){
                dfs(x,cnt,tot);
            }
        }
    }
    
    int main(){
        cin>>t;
        for(int cs=1;cs<=t;++cs){
            cin>>n>>m;
            init();
            int u,v;
            
            rep(i,1,m){
                scanf("%d%d",&u,&v);
                g[u].push_back(v);
                g[v].push_back(u);
                in[u]++;
                in[v]++;
            }
            ll cnt=0;
            int tot=0;
            rep(i,1,n){
                if(!vis[i]){
                    cnt=0;
                    tot=0;
                    dfs(i,cnt,tot);
                    cnt=max(0ll,cnt/2-tot);
                    ans+=cnt;
                }
            }
            printf("Case #%d: %lld
    ",cs,ans);
        }
        return 0;
    }
    View Code

     这题队友Y当时很快想出了正解,但是没有考虑图不连通的情况,然后队友H就提议用SCC来写,然后因为手滑失误+调板子,成功+3


     G Good Number 简单数学

    题意:给定$n$和$k$,求不超过$n$的$i$使得$i$能整除$lfloorsqrt[k]{i} floor$,求$i$的个数

    思路:枚举$i$,统计$left[i^{k},(i+1)^{k} ight)$能被$i$整除的个数,求和

    代码:

    #include <bits/stdc++.h>
    
    #define debug(...)  fprintf(stderr,__VA_ARGS__)
    #define DEBUG printf("Passing [%s] in LINE %d
    ",__FUNCTION__,__LINE__)
    #define Debug debug("Passing [%s] in LINE %d
    ",__FUNCTION__,__LINE__)
    #define rep(i,x,y) for(int i=(x);i<=(y);++i)
    #define _max(a,b) (a)>(b)? (a):(b)
    #define _min(a,b) (a)<(b)? (a):(b)
    
    using namespace std;
    typedef long long ll;
    typedef unsigned long long ull;
    typedef pair<int,int> pii;
    typedef pair<ll,int> pli;
    typedef pair<int,ll> pil;
    typedef pair<ll,ll> pll;
    
    int t;
    ll n,k;
    const ll MAXN = 1e9+10;
    ll qpow(ll a,ll b){
        ll ret=1;
        if(a==1)    return 1;
        for(ll i=1;i<=b;++i){
            ret=ret*a;
            if(ret>n)    return 0;
        }
        return ret;
    }
    
    int main(){
        cin>>t;
        for(int cs=1;cs<=t;++cs){
            cin>>n>>k;
            if(k==1){
                printf("Case #%d: %lld
    ",cs,n);
                continue;
            }
            ll l=1,r=1;
            ll ans=0;
            bool fl=0;
            for(ll i=1;i<n;++i){
                l=qpow(i,k),r=qpow(i+1,k);
                if(r==0){
                    fl=1;
                    r=n+1;
                }
                ans+=(r-l+i-1)/i;
                //cout<<i<<" "<<ans<<endl;
                if(fl)  break;
            }
            printf("Case #%d: %lld
    ",cs,ans);
        }
        return 0;
    }
    View Code

    比赛的时候我还在读F的时候,队友H和Y就已经想出正解了,队友H直接上机拍代码1A


    K Kingdom's Power 树形dp

    题意:给定一棵树,根节点有无数个人,每一个单位时间,可以使一个人移动到一个相邻节点,求走完整棵树的最小时间花费

    思路:首先,对于一个人,不考虑根节点派出一个人,肯定是从深度小的子树转移到深度大的子树才可能更优,先求每一颗子树的深度,然后对每颗子树的根节点的子结点的子树深度排序。其次,一颗子树被走完所派出的人要么是从兄弟子树来的,要么是从整棵树的根节点来的,在更新了一个子节点之后,需要更新可能传给下一个节点的值,但是这个值只能用一次,因此需要注意一下细节。(可能解释的不是很清楚,可以看一下参考博客:https://www.cnblogs.com/crazyfz/p/13838422.html)

    代码:

    #include <bits/stdc++.h>
    
    #define debug(...)  fprintf(stderr,__VA_ARGS__)
    #define DEBUG printf("Passing [%s] in LINE %d
    ",__FUNCTION__,__LINE__)
    #define Debug debug("Passing [%s] in LINE %d
    ",__FUNCTION__,__LINE__)
    #define rep(i,x,y) for(int i=(x);i<=(y);++i)
    #define _max(a,b) (a)>(b)? (a):(b)
    #define _min(a,b) (a)<(b)? (a):(b)
    
    using namespace std;
    typedef long long ll;
    typedef unsigned long long ull;
    typedef pair<int,int> pii;
    typedef pair<ll,int> pli;
    typedef pair<int,ll> pil;
    typedef pair<ll,ll> pll;
    
    const int MAXN=1e6+10;
    
    int t,n;
    ll ans=0;
    
    int val[MAXN];
    int fa[MAXN];
    vector<pii> g[MAXN];
    
    void init(){
        for(int i=0;i<=n;++i)   g[i].clear();
        ans=0;
    }
    
    int getdep(int u){
        if(g[u].empty())    return 1;
        for(int i=0;i<g[u].size();++i){
            g[u][i].first=getdep(g[u][i].second);
        }
        sort(g[u].begin(),g[u].end());
        return g[u].back().first+1;
    }
    
    int dfs(int u,int d,int v){
        val[u]=v;
        if(g[u].empty())    return 1;
        int res=v;
        for(int i=0;i<g[u].size();++i){
            res=min(d,dfs(g[u][i].second,d+1,res+1));
        }
        return res+1;
    }
    
    int main(){
        cin>>t;
        for(int cs=1;cs<=t;++cs){
            scanf("%d",&n);
            init();
            int x;
            rep(i,2,n){
                scanf("%d",fa+i);
                g[fa[i]].push_back(pii(0,i));
            }
            getdep(1);
            dfs(1,0,0);
            rep(i,1,n)  if(g[i].empty()) printf("%d->%d
    ",i,val[i]);
            rep(i,1,n)  if(g[i].empty()) ans+=val[i];
            printf("Case #%d: %lld
    ",cs,ans);
        }
        return 0;
    }
    View Code

    比赛的时候过了铜牌题之后我们队开了C和K,然后我们重点rush C,rush失败,K当时我也和队友Y讨论了蛮久,一直看不出来考的什么,我们当时也只讨论出来需要预处理子树深度,然后想着有什么贪心的方法解决,最后思路确实不大清晰,只能把重心放在C上。


    比赛总结,A签到,队友说思路我写,可能因为紧张,码的磕磕绊绊,还好1A了;G,队友H和Y的输出,1A;F,队友H的做法有点小题大做了,中间一些小失误,+3;E,跟队友Y讨论了思路,我码,数据结构维护部分扔给了队友H,写完之后还测了好几组数据,确认没问题就交了,1A;最后我们看榜,有C和K是比较可做的,然后我读C,队友H和Y读K,我读完题跟Y和H交流了题意,认为C就是极角排序,就让队友H上机写了,我跟队友Y讨论K,无果,我们就都去帮队友H debug( 结对编程(bushi )。比赛结束,摸到了很极限的铜尾。

    跟两个队友第一次组队就能摸一块铜,看来以后得抓紧训练了(

     

     
  • 相关阅读:
    python --github 刷题
    http://www.rehack.cn/techshare/webbe/php/3391.html
    SQL 百万级数据提高查询速度的方法
    开学收好这 17 种工具 App,让你新学期学习更有效率
    Git文件常见下标符号说明
    TortoiseGit功能介绍
    gitlab图形化使用教程 (mtm推荐)
    gitlab 服务器的搭建与使用全过程(一)
    Git详解之一 Git实战
    Git使用基础篇
  • 原文地址:https://www.cnblogs.com/JNzH/p/13852667.html
Copyright © 2020-2023  润新知