【Codeforces #133 Div2】Solutions
【吐槽】
1.发烧打吊瓶,晚上坚持不住睡着了,没赶上比赛……
2.CF的官方英文题解出的真是够慢的……
3.cin cout什么的能用就用了管他慢不慢……我就懒死……
==============================================================================================
【A Tiling with Hexagons】
http://codeforces.ru/contest/216/problem/A
题目大意:对边平行且相等的大六边形里面排列若干小六边形,大六边形的边长n表示在这条边的方向上排列着n个小六边形。看图吧= =
a=2 , b=3 , c=4
初看这个题没什么思路,也不好讲……但是在Comment里面看到一个很不错的图片,解法一目了然。
答案为a*b+b*c+a*c-a-b-c+1
#include <iostream> using namespace std; int a,c,b; int main(){ cin>>a>>b>>c; cout<<a*b+a*c+b*c-a-b-c+1<<endl; }
【B Forming Teams】
http://codeforces.ru/contest/216/problem/B
题目大意:足球赛要分成两组,有些人之间有仇恨关系不能分在一组,且仇恨关系有传递相互性。当无法完成分组时,就要让一部分人坐在替补席上不上场。问最少让几个人不上场。
构图之后,会出现环、链、联通块什么的。发现不能分组的情况只出现在奇环,如果出现奇环,那就要让一个人下去坐着。并查集维护即可。
注意的地方:1.题目有关于点的度数最大为2的提示,比较隐蔽。
2.处理完所有奇环之后还要看剩下的人数能不能平均分开。
#include <iostream> using namespace std; int fa[110],size[110],ans,n,m; int find(int x){ if(x==fa[x]) return x; else return fa[x]=find(fa[x]); } int main(){ cin>>n>>m; for(int i=1;i<=n;i++) fa[i]=i,size[i]=1; while(m--){ int x,y; cin>>x>>y; int fx=find(x),fy=find(y); if(fx==fy && (size[fx]&1)) ans++; fa[fy]=fx,size[fx]+=size[fy]; } if((n-ans)&1) ans++; cout<<ans<<endl; }
【C Hiring Staff】
http://codeforces.ru/contest/216/problem/c
题目大意:老板要雇服务员,服务员工作n天,休息m天,如此往复。店只有一把钥匙,拿钥匙的员工如果第二天休息,就要在工作最后一天把钥匙传给别人……还得保证每天店里至少k个服务员,问最少雇多少,什么时间雇。
这道题显然满足贪心的性质,提前雇员工没有意义,摆在那也不能使决策更优,所以在需要的时候雇员工即可。雇员工的条件有两个:1.员工不够;2.没有员工明天来开门。
#include <iostream> #include <vector> using namespace std; vector<int> ans; int n,m,k,E[2020],delta; int main(){ cin>>n>>m>>k; for(int i=1;i<=n+m;i++){ delta=0; if(E[i]<k) delta=k-E[i]; if(!E[i+1] && !delta) delta=1; if(delta){ for(int j=i;j<i+n;j++) E[j]+=delta; while(delta--) ans.push_back(i); } } cout<<ans.size()<<endl; for(int i=0;i<ans.size();i++) cout<<ans[i]<<" "; }
【D Spider's Web】
http://codeforces.ru/contest/216/problem/D
题目大意:某蜘蛛结了如下的网……每个小块左右两边(姑且这么叫吧)的交点数量相等,这个块就被成为stable(greed area),否则就是unstable(red area)。问unstable的数量。
题挺水的,枚举每一个块,用他的内径外径作为限制,在相邻两边统计焦点数目。
如何统计?二分答案,找到上下边界,中间的元素个数就出来了。
C++党使用STL瞬间减少若干行。。。
#include <iostream> #include <algorithm> #include <vector> using namespace std; vector<int> dist[1000]; int n,m,x,ans; int main(){ cin>>n; for(int i=0;i<n;i++){ cin>>m; while(m--){ cin>>x; dist[i].push_back(x); } sort(dist[i].begin(),dist[i].end()); } for(int i=0;i<n;i++){ int last=(i+n-1)%n,next=(i+1)%n; for(int j=1;j<dist[i].size();j++){ int low=dist[i][j-1],high=dist[i][j]; int P_last=upper_bound(dist[last].begin(),dist[last].end(),high)-lower_bound(dist[last].begin(),dist[last].end(),low); int P_next=upper_bound(dist[next].begin(),dist[next].end(),high)-lower_bound(dist[next].begin(),dist[next].end(),low); ans+=(P_last!=P_next); } } cout<<ans<<endl; }
【E Martian Luck】【UPD2】
http://codeforces.ru/contest/216/problem/E
题目大意:一个数字的数字根定义为在k进制下各位数字相加,重复若干次,得到的一位数。如果一个数字的数字根为b,那么这个数字就很lucky。给你一个数字串,问有多少个连续字串组成的数字是lucky的。
首先必须了解数字根。数字根有以下性质(跟这个题相关的):
1.一个数x加9的数字根是x的数字根。比如:8+9=17,R(17)=8;12+9=21,R(21)=R(12).
2.一个数x乘9的数字根是9.比如:6*9=54,R(54)=9.
3.若A+B=C,则R(a)+R(b)=R(c).
证明:由性质(1)(2)可将每个数字分解为若干9与其数字根的和,A=9n+R(A) , B=9m+R(B) , C=9p+R(C) .
则R(A)+R(B)=R(C)+9(p-m-n) .再由性质(1)(2)知9的倍数对数字根没有影响,得证。
由性质3,我们在这道题求解字串数字根的事时候就可以用前缀和维护。
由于题目允许前导0,所以在统计的时候还需要统计0对答案的影响。
当b=0时,答案就是选取0的方案数;b=k-1时,由于取模运算会出现0,所以要防止0被重复计算。
顺次统计每一位,f(sum)=(sum-b) mod (k-1),我用了一个map记录数字根出现的次数方便查找。
#include <iostream> #include <map> using namespace std; map<int,int> m; long long ans,sum; int k,b,n,x,cnt; int main(){ cin>>k>>b>>n; if(!b){ for(int i=0;i<n;i++){ cin>>x; cnt=x?0:(cnt+1); ans+=cnt; } }else{ k--,m[0]=1; for(int i=0;i<n;i++){ cin>>x; sum+=x; cnt=x?0:(cnt+1); ans+=m[(sum-b+k)%k]; m[sum%=k]++; if(b==k) ans-=cnt; } } cout<<ans<<endl; }