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 }
比赛的时候顺利签到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; }
比赛的时候最后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; }
比赛的时候队友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; }
这题队友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; }
比赛的时候我还在读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; }
比赛的时候过了铜牌题之后我们队开了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 )。比赛结束,摸到了很极限的铜尾。
跟两个队友第一次组队就能摸一块铜,看来以后得抓紧训练了(