Educational Codeforces Round 58 (Rated for Div. 2) 题解
Educational Codeforces Round 58 (Rated for Div. 2)
题目总链接:https://codeforces.com/contest/1101
A. Minimum Integer
题意:
多组数据,给你三个数l,r,d,要求在区间[l,r]之外找一个最小的x,使得x%d==0。
题解:
当d<l or d>r的时候,直接输出d就好了。
当l<=d<=r的时候,找到最小的t,使得t*d>r就行了。
具体操作见代码:
#include <bits/stdc++.h> using namespace std; typedef long long ll; int q; ll l,r,d; int main(){ cin>>q; while(q--){ cin>>l>>r>>d; if(d<l || d>r) cout<<d<<endl; else{ ll now = r/d; cout<<(now+1)*d<<endl; } } return 0; }
B. Accordion
题意:
给出一个字符串,要求删去一些数后它由以下几个部分组成,[ : .... : ],这其中"...."指的是0个或多个“ | ”。
问满足上面条件时,留下的字符串的最大长度为多少。
题解:
模拟一下就好了,从左往右遍历找 [ 以及 :
然后再从右往左遍历找 ] 以及 :
最后找中间的 |
这只是大体思路,代码还需要判断这些是否存在(我就是没判断这个被hack了...)、是否满足条件。
代码如下:
#include <bits/stdc++.h> using namespace std; typedef long long ll; const int N = 5e5+5; char s[N]; int main(){ scanf("%s",s); int len = strlen(s); int j=len,ans=0; for(int i=0;i<len;i++){ if(j==len){ if(s[i]=='[') ans++,j--; continue ; }else if(j==len-1){ if(s[i]==':'){ ans++; j=i; break ; } } } int k=0; for(int i=len-1;i>=0;i--){ if(k==0){ if(s[i]==']') ans++,k++; continue ; }else if(k==1){ if(s[i]==':'){ ans++; k=i; break ; } } } if(j>=k) puts("-1"); else{ for(int i=j+1;i<k;i++){ if(s[i]=='|') ans++; } cout<<ans<<endl; } return 0; }
C. Division and Union
题意:
给出n个区间,然后将这些区间分到两个集合S1,S2中,要求S1与S2的交集为空。最后输出区间是属于1集合还是2集合。
题解:
一开始想歪了,用并查集去做,虽然也可以做,但是有点麻烦了。
分析一下就可以发现,相交的区间肯定放在一个集合中,不相交的区间可以放在一个集合中,也可以放在另外一个集合中。
那么我们直接按左端点排序后,贪心地将前n-1个区间放在一个集合中,判断第n个区间放入另一个集合是否满足条件就ok了。
注意的是放入一个集合的时候,需要维护右端点的最大值,这样最后比较的时候才能保证正确性。
代码如下:
#include<bits/stdc++.h> #define INF 0x3f3f3f3f using namespace std; typedef long long ll; const int N = 2e5+5; int n; struct Seg{ int l,r,id; bool operator < (const Seg &A)const{ return l<A.l; } }seg[N]; int T; int ans[N],a[N]; int main(){ cin>>T; while(T--){ scanf("%d",&n); for(int i=1;i<=n;i++){ scanf("%d%d",&seg[i].l,&seg[i].r); seg[i].id=i; a[i]=0; } sort(seg+1,seg+n+1); int t=1; int max_r = 0; a[0]=1; for(int i=1;i<n;i++){ max_r = max(max_r,seg[i].r); if(!a[i]) a[i]=a[i-1]; if(max_r<seg[i+1].l){ t++; a[i+1]=t; } } if(!a[n]) a[n]=a[n-1]; if(t<2) puts("-1"); else{ for(int i=1;i<=n;i++){ if(a[i]<t) ans[seg[i].id]=1; else ans[seg[i].id]=2; } for(int i=1;i<=n;i++) printf("%d ",ans[i]); printf("\n"); } } return 0; }
D. GCD Counting
题意:
给出一颗树,每个结点有相应的权值,现在定义g(x,y)为树上x到y的简单路径的所有结点权值的最大公约数,dis(x,y)为x到y路径上面点的个数。求最大的dis(x,y)且满足g(x,y)>1。
题解:
说说比较朴素的方法吧...虽然速度比较慢,但对于我这种蒟蒻比较好懂...
对于2到2e5中的每个数,找出以其为因子的所有点,这些点不一定是连通的,最终呈现出来的应该是多个连通块。
然后我们直接对每个点跑最大直径就行了。
这个算法的具体时间复杂度我也不太清楚,但应该比较高,有方法可以将算法优化到nlogn的级别...
代码如下:
#include <bits/stdc++.h> using namespace std; typedef long long ll; const int N = 2e5+5; int a[N],vis[N]; int ans,n; vector <int> g[N],vec[N]; int dfs(int u){ vis[u]=0; int mx = 0; for(int v:g[u]){ if(vis[v]){ int now = dfs(v); ans = max(ans,now+1+mx); mx = max(mx,now); } } ans=max(ans,1); return mx+1; } int main(){ scanf("%d",&n); for(int i=1;i<=n;i++){ scanf("%d",&a[i]); for(int j=1;j*j<=a[i];j++){ if(a[i]%j) continue ; vec[j].push_back(i); if(j*j!=a[i]) vec[a[i]/j].push_back(i); } } for(int i=1;i<n;i++){ int u,v; scanf("%d%d",&u,&v); g[u].push_back(v); g[v].push_back(u); } for(int i=2;i<=2e5;i++){ for(int v:vec[i]) vis[v]=1; for(int u:vec[i]) if(vis[u]) dfs(u); } printf("%d",ans); return 0; }
E. Polycarp's New Job
题意:
在线给出一些矩形的长和宽,以及在线询问:给出一个矩形的箱子,问能否将之前所有给出的矩形装进去。
题解:
这是E题的难度么...感觉比C题还简单点。
对于给出的长和宽,让短边在前,长边在后。然后维护最长短边以及最长长边就行了。因为我们放的时候的最优选择肯定是短边放短边的。
最后询问的时候判断下就ok了...
#include <bits/stdc++.h> using namespace std; typedef long long ll; const int N = 5e5+5; int n; int main(){ cin>>n; int l = 0,r = 0; getchar(); while(n--){ char c; int x,y; scanf("%c %d %d",&c,&x,&y); if(x>y) swap(x,y); if(c=='+'){ l=max(l,x); r=max(r,y); }else{ if(x<l || y<r) puts("NO"); else puts("YES"); } getchar(); } return 0; }
G. (Zero XOR Subset)-less
题意:
给出n个数,要求尽量多地将这些数划分为连续地几段,并且满足任意子集地异或和不为0。
题解:
只要所有数的异或和都不为0,那么就必然存在一种划分方法,通过这个可以判断可行性。
根据异或的性质,考虑异或前缀和,那么任意一段数的异或和都可以用两个前缀和异或来表示。
现在问题就转化为了:在n个数中选取尽量多的数,使得这些数的任意子集的异或和不为0。
这是个线性基的经典问题,关于线性基,可以参考下这篇博客:https://www.cnblogs.com/ljh2000-jump/p/5869991.html
线性基有几个性质,其中包括:一是线性基的任意子集异或和都不为0;二是线性基的任意子集异或和的值域等于原数的任意子集异或和的值域。
那么,最终的答案就是前缀异或和中线性基的个数。
代码如下:
#include <bits/stdc++.h> using namespace std; typedef long long ll; const int N = 2e5+5; int a[N],p[N],sum[N]; int n; int main(){ scanf("%d",&n); for(int i=1;i<=n;i++){ scanf("%d",&a[i]); sum[i]=sum[i-1]^a[i]; } if(!sum[n]){ cout<<-1; return 0; } for(int i=1;i<=n;i++){ for(int j=29;j>=0;j--){ if(!((1<<j)&sum[i])) continue ; if(!p[j]){ p[j]=sum[i]; break ; } sum[i]^=p[j]; } } int ans = 0; for(int i=0;i<30;i++) if(p[i]) ans++; cout<<ans; return 0; }
重要的是自信,一旦有了自信,人就会赢得一切。