Codeforces Hello2020 A-E简要题解
contest链接:https://codeforces.com/contest/1284
A. New Year and Naming
思路:签到,字符串存一下,取余拼接。
1 #include<iostream> 2 #include<vector> 3 #include<cstring> 4 #include<map> 5 #include<algorithm> 6 using namespace std; 7 const int maxn = 1e+5; 8 int main(){ 9 vector<string> s1; 10 vector<string> s2; 11 int n,m;cin>>n>>m; 12 for(int i = 0;i<n;i++){ 13 string t;cin>>t; 14 s1.push_back(t); 15 } 16 for(int i = 0;i<m;i++){ 17 string t;cin>>t; 18 s2.push_back(t); 19 } 20 int q;cin>>q; 21 while(q--){ 22 long long y;cin>>y; 23 cout<<s1[(y-1)%n]+s2[(y-1)%m]<<endl; 24 } 25 return 0; 26 }
B. New Year and Ascent Sequence
题意:定义上升序列Ascent,在一组序列A中,存在1<i<j<n,使得Ai<Aj。现在给定n个序列,求n个序列两两组合的n^2个组合序列中,有多少个组合是Ascent序列。。
思路: 用两个MAX,MIN数组分别记录下每个序列的最大值和最小值,然后把最大值进行排序。每次做一遍二分,取每个数组中的最小值到所有数组中的最大值中找有多少个数比它大,这样组合的新序列便是满足Ascent性质。如果本身这个序列就已经满足Ascent的性质,那么直接把最大值设置为9999999,最小值设置为-1存进去。
1 #include<iostream> 2 #include<vector> 3 #include<cstring> 4 #include<map> 5 #include<algorithm> 6 using namespace std; 7 vector<int> MAX; 8 vector<int> MIN; 9 int main(){ 10 int t; 11 cin>>t; 12 for(int i = 0;i<t;i++){ 13 int Length; 14 cin>>Length; 15 long long Min = 999999999; 16 long long Max = -1; 17 int ok = 0; 18 for(int j = 0;j<Length;j++){ 19 long long cur; 20 cin>>cur; 21 if(cur> Min && ok == 0){ 22 ok = 1; 23 } 24 Min = min(Min,cur); 25 Max = max(Max,cur); 26 } 27 if(ok == 0){ 28 MAX.push_back(Max); 29 MIN.push_back(Min); 30 } 31 else{ 32 MAX.push_back(9999999); 33 MIN.push_back(-1); 34 } 35 } 36 sort(MAX.begin() ,MAX.end() ); 37 long long ans = 0; 38 for(int i = 0;i<t;i++){ 39 ans+=(MAX.end() -upper_bound(MAX.begin() ,MAX.end() ,MIN[i])); 40 //cout<<ans<<endl; 41 } 42 cout<<ans; 43 return 0; 44 }
C. New Year and Permutation
题意:定义一个framed segment,在区间[l,r]中,max值-min值 = r - l。求有1-n 组成的序列中,所有framed segment的个数%m
思路:组合数学推一个结论。例如假设1到n组成的序列中,求长度为k的framed segment,那么其一段序列的最大值 - 最小值 = k,例如n = 5,k = 3,这些framed segment 必定是 1 2 3 或者2 3 4 或者 3 4 5,可以观测到其长度为k的framed segment必定是连续的,可以把他们单独算一个整体,这样序列总体长度变为n - k + 1,内部长度为k,内部组合种类就是k!个,总体组合种类就是(n-k+1)!,长度为k的framed segment种类又是(123,234,345)n - k + 1种,所以长度为k的framed segment 最终答案就是(n-k+1)*(n-k+1)!*k!,预处理一下阶乘即可。
1 #include<iostream> 2 #include<cstdio> 3 #include<cstring> 4 #include<algorithm> 5 #include<vector> 6 #include<queue> 7 using namespace std; 8 typedef long long ll; 9 const int maxn = 2e5+5e4+5; 10 ll fac[maxn]; 11 int main(){ 12 ll n,m; 13 cin>>n>>m; 14 fac[1] = 1; 15 for(int i = 2;i<=n;i++){ 16 fac[i] = (fac[i-1]*i)%m; 17 } 18 ll ans = 0; 19 for(int i = 1;i<=n;i++){ 20 ans +=((n+1-i)*(fac[i])%m)*(fac[n+1-i])%m; 21 ans = ans%m; 22 } 23 // 1 2 3 24 cout<<ans%m; 25 return 0; 26 }
D. New Year and Conference
题意:有n场讲座,有两个场地a和b,如果在a场地开讲座则需要占用[sai,eai],在b场地开讲座则需要占用[sbi,ebi]这个时间段,假如开两场讲座,如果在a场地开不冲突,而b场地开冲突,则称其为敏感的,同理a和b反过来也是一样的,如果ab两场地都冲突则也不是敏感的,先求给定的n场讲座,任意的两两讲座是否在ab场地敏感。
思路:暴力枚举是O(n^2)复杂度,必定超时,这里可以用线段树或ST表做,达到一个nlogn的复杂度。对于任意一个讲座x,找出所有与x讲座在a场地冲突的讲座,再判断其是否在b场地冲突,如果不是则直接输出“NO”
首先对所有讲座的sa和ea进行升序(sa为a场地开始时间,ea为a场地结束时间,sb和eb就是b场地开始结束时间),例如讲座 i 的时间片是[x,y],在其时间片上与 i 在a场地冲突的讲座用二分的方法可以枚举出来一个离散化后的区间,然后我们用线段树维护b场地的sb最大值和eb最小值,每次查询出这个区间的的sb最大值和eb最小值,此时如果说sb最大值 > y 或者 eb最小值 < x,那么这些表演中必定存在b场地与表演i不冲突的情况,此时直接输出“NO”,把所有的讲座都check一遍,交换ab次序再check即可,上述只检验了在a场地冲突一定在b场地冲突的情况,但并未考虑在b场地冲突而在a场地不冲突的情况。
总体时间复杂度nlogn。
AC代码:
1 #include<iostream> 2 #include<vector> 3 #include<cstdlib> 4 #include<cstdio> 5 #include<algorithm> 6 #include<cmath> 7 #include<cstring> 8 #include<queue> 9 #include<map> 10 using namespace std; 11 typedef long long ll; 12 const int maxn = 1e5+200; 13 struct node{ 14 int sa,ea,sb,eb; 15 node(){}; 16 node(int a,int b,int c,int d){ 17 sa = a,ea = b,sb = c,eb = d; 18 } 19 bool operator<(node cur)const{ 20 if(sa == cur.sa ) return ea<cur.ea ; 21 return sa < cur.sa ; 22 } 23 }point[maxn]; 24 int segt_max[4*maxn],segt_min[4*maxn]; 25 void build(int l,int r,int k){//建两个线段树维护区间最大最小值 26 if(l == r) { 27 segt_max[k] = point[l].sb; 28 segt_min[k] = point[l].eb; 29 return ; 30 } 31 int mid = (l+r)/2; 32 build(l,mid,2*k); 33 build(mid+1,r,2*k+1); 34 segt_max[k] = max(segt_max[2*k],segt_max[2*k+1]); 35 segt_min[k] = min(segt_min[2*k],segt_min[2*k+1]); 36 } 37 int queryMin(int l,int r,int al,int ar,int k){//查询区间最小值 38 if(l >= al && r <= ar) return segt_min[k]; 39 int mid = (l+r)/2; 40 if(ar<=mid) return queryMin(l,mid,al,ar,2*k); 41 else if(al>mid){ 42 return queryMin(mid+1,r,al,ar,2*k+1); 43 } 44 else { 45 return min(queryMin(l,mid,al,mid,2*k),queryMin(mid+1,r,mid+1,ar,2*k+1)); 46 } 47 } 48 int queryMax(int l,int r,int al,int ar,int k){//查询区间最大值 49 if(l >= al && r <= ar) return segt_max[k]; 50 int mid = (l+r)/2; 51 if(ar<=mid) return queryMax(l,mid,al,ar,2*k); 52 else if(al>mid){ 53 return queryMax(mid+1,r,al,ar,2*k+1); 54 } 55 else { 56 return max(queryMax(l,mid,al,mid,2*k),queryMax(mid+1,r,mid+1,ar,2*k+1)); 57 } 58 } 59 bool check(int n){ 60 sort(point+1,point+1+n); 61 build(1,n,1); 62 for(int i = 1;i<=n;i++){ 63 int pos = lower_bound(point+1,point+1+n,node(point[i].ea ,1e9+5000,0,0))-point-1; 64 //查找与第i场演讲冲突的集合 65 if(i+1>pos) continue;//无冲突直接跳过 66 //check一下另一个场地的所有表演是否都冲突 67 if(queryMin(1,n,i+1,pos,1)<point[i].sb || queryMax(1,n,i+1,pos,1)>point[i].eb){ 68 return false; 69 } 70 } 71 return true; 72 } 73 int main() 74 { 75 int n; 76 cin>>n; 77 int f = 0; 78 for(int i = 1;i<=n;i++){ 79 cin>>point[i].sa>>point[i].ea>>point[i].sb>>point[i].eb; 80 } 81 if(check(n)) f++; 82 for(int i = 1;i<=n;i++){ 83 swap(point[i].sa,point[i].sb); 84 swap(point[i].ea,point[i].eb); 85 } 86 if(check(n)) f++; 87 if(f == 2) cout<<"YES"; 88 else cout<<"NO"; 89 return 0; 90 }
E. New Year and Castle Construction
题意:平面上有n个点,问你存在多少组四个点围成的四边形 严格包围某个点P的情况。不存在三点共线。
思路:首先看数据范围是2500,可以做n^2的枚举,我们可以枚举两遍n。正面求解有些困难,反面求解可以考虑有多少组子集不满足题目要求,拿总的子集数量减去不满足的就是答案。
那么考虑不满足题意的点集。
首先如果对于一对点P和P1,让它们连线,以P为基准,相对度数为0度,枚举P和P1这个条线以上的所有点,假设枚举出n个点,n个点加P1,从中选出4个点,这4个点组成的四边形必定不含点P,以此性质就可以做了。
首先做一遍极角排序,然后固定一条边,以这条边为0度,开始扫描这条边0到180度的所有点,然后从中任意选出3个点C(n,3),与P1组成的四边形便不封闭包围P1,统计所有不满足条件的点集,用总体点集
个数C(n,5)- 不满足的组合个数 就是答案。
如图所示,P1,P2,P3,P4组成的四边形不封闭包围点P。
AC代码:
1 #include<iostream> 2 #include<vector> 3 #include<cstdlib> 4 #include<cstdio> 5 #include<algorithm> 6 #include<cmath> 7 #include<cstring> 8 #include<queue> 9 #include<map> 10 using namespace std; 11 typedef long long ll; 12 const long double pi = acos(-1.0L); 13 const int maxn = 2e3+600; 14 long double x[maxn],y[maxn]; 15 int main() 16 { 17 int n;cin>>n; 18 ll ans = (ll)n*(n-1)*(n-2)*(n-3)*(n-4)/24; //计算C(n,5) 19 for(int i = 0;i<n;i++){ 20 ll xi,yi; 21 cin>>xi>>yi; 22 x[i] = xi,y[i] = yi; 23 } 24 for(int i = 0;i<n;i++){ 25 vector<long double> v; 26 for(int j = 0;j<n;j++){ 27 if(i == j) continue; 28 v.push_back(atan2(y[j]-y[i],x[j]-x[i])); //算一下角度 29 } 30 sort(v.begin(),v.end());//做极角排序 31 int k = n - 1 , cur = 0; 32 for(int j = 0;j<k;j++){ 33 while(cur < j+k){ //枚举点个数增加一倍 34 long double angle = v[cur%k] - v[j];//枚举两条边的夹角 35 if(angle < 0) angle+=2*pi;//如果角度小于0,则增加2*pi转一圈 36 if(angle < pi) cur++;//如果该点在枚举的这条线上面,则cur++ 37 else break; 38 } 39 long long cnt = cur - j - 1; 40 ans-=1ll*cnt*(cnt-1)*(cnt-2)/6; 41 } 42 } 43 cout<<ans; 44 return 0; 45 }