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\n",__FUNCTION__,__LINE__)
 5 #define Debug debug("Passing [%s] in LINE %d\n",__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\n",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\n",cs,fz/re,fm/re);
30         }
31     }
32     return 0;
33 }
View Code

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

 


 

C Cameraman

题意:一个$W\times 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)\rarr(dx,dy)\rarr(0,dy)\rarr(cx,dy)\rarr(cx,0)\rarr(cx,cy)\rarr(0,cy)\rarr(dx,cy)\rarr(dx,0)$,进行简单的三角函数运算,特判一下最后一个跟第一个所围成的长度

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

#include <bits/stdc++.h>

#define debug(...)  fprintf(stderr,__VA_ARGS__)
#define DEBUG printf("Passing [%s] in LINE %d\n",__FUNCTION__,__LINE__)
#define Debug debug("Passing [%s] in LINE %d\n",__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\n",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\n",__FUNCTION__,__LINE__)
#define Debug debug("Passing [%s] in LINE %d\n",__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\n",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\n",__FUNCTION__,__LINE__)
#define Debug debug("Passing [%s] in LINE %d\n",__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\n",cs,ans);
    }
    return 0;
}
View Code

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

 


 

 G Good Number 简单数学

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

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

代码:

#include <bits/stdc++.h>

#define debug(...)  fprintf(stderr,__VA_ARGS__)
#define DEBUG printf("Passing [%s] in LINE %d\n",__FUNCTION__,__LINE__)
#define Debug debug("Passing [%s] in LINE %d\n",__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\n",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\n",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\n",__FUNCTION__,__LINE__)
#define Debug debug("Passing [%s] in LINE %d\n",__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\n",i,val[i]);
        rep(i,1,n)  if(g[i].empty()) ans+=val[i];
        printf("Case #%d: %lld\n",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 )。比赛结束,摸到了很极限的铜尾。

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

 

 
posted @ 2020-10-22 20:31  JNzH  阅读(1556)  评论(0编辑  收藏  举报