Educational Codeforces Round 87 (Rated for Div. 2) a~e
又差一点点。
A. Alarm Clock
题意:有一个人,他从0时刻开始睡觉,当他累计睡够a分钟时,睡眠结束。他只有在听到闹钟声时才会醒来,第一个闹钟在b时刻。任何时刻,当他醒来后如果发现还没有睡够a分钟,就会将闹钟后调c分钟,然后继续准备睡觉,准备d分钟后正式进入睡眠状态。如果准备阶段闹钟响了,会和睡着时闹钟响了情况一样。问他能否睡够,如果能睡够一共睡了多久。
思路:如果第一次醒来就睡够了,那最终答案就是b。如果c<=d那么自从第一次醒来之后就永远无法睡着。否则总可以睡够a分钟,即(a-b)/(c-d)的上取整次"调整闹钟操作"。每次操作花费的时间为c。
1 #include<bits/stdc++.h> 2 #define LL long long 3 #define dl double 4 void rd(int &x){ 5 x=0;int f=1;char ch=getchar(); 6 while(ch<'0' || ch>'9'){if(ch=='-')f=-1;ch=getchar();} 7 while(ch<='9' && ch>='0')x=x*10+ch-'0',ch=getchar();x*=f; 8 } 9 void lrd(LL &x){ 10 x=0;int f=1;char ch=getchar(); 11 while(ch<'0' || ch>'9'){if(ch=='-')f=-1;ch=getchar();} 12 while(ch<='9' && ch>='0')x=x*10+ch-'0',ch=getchar();x*=f; 13 } 14 const int INF=1e9; 15 const LL LINF=1e18; 16 using namespace std; 17 int T,a,b,c,d; 18 int main(){ 19 // freopen("in.txt","r",stdin); 20 rd(T); 21 while(T--){ 22 rd(a);rd(b);rd(c);rd(d); 23 if(b >= a)printf("%d\n",b); 24 else if(c <= d)printf("-1\n"); 25 else printf("%lld\n",b+(LL)(ceil(1.0*(a-b)/(c-d))*c)); 26 } 27 return 0; 28 } 29 /**/
B. Ternary String
题意:给一个由字符'1' '2' '3'组成的字符串(2e5),寻找一个连续子串包含同时包含这三个数,求其长度的最小值。
思路:
两个思路
我的:不用动脑子的二分,也不用看啥性质,可以发现最终答案有单调性,直接二分就好了,判断是否包含只需要用类似于"单调队列"的形式扫一遍维护当前123的个数即可。
答案:可以证明最终答案一定是abbbb...bbbc这种样式,因为如果bbb...bbb中间出现了a或者c,那么一定可以断开找到一个更短的合法情况。于是只需要找到所有段连续相同的数字,并对其两侧进行检测即可。
1 #include<bits/stdc++.h> 2 #define LL long long 3 #define dl double 4 void rd(int &x){ 5 x=0;int f=1;char ch=getchar(); 6 while(ch<'0' || ch>'9'){if(ch=='-')f=-1;ch=getchar();} 7 while(ch<='9' && ch>='0')x=x*10+ch-'0',ch=getchar();x*=f; 8 } 9 void lrd(LL &x){ 10 x=0;int f=1;char ch=getchar(); 11 while(ch<'0' || ch>'9'){if(ch=='-')f=-1;ch=getchar();} 12 while(ch<='9' && ch>='0')x=x*10+ch-'0',ch=getchar();x*=f; 13 } 14 const int INF=1e9; 15 const LL LINF=1e18; 16 const int N=2e5+10; 17 using namespace std; 18 int T,n; 19 char s[N]; 20 int cnt[3]; 21 bool check(int x){ 22 cnt[0]=cnt[1]=cnt[2]=0; 23 for(int i=0;i<n;i++){ 24 cnt[s[i]-'1']++; 25 if(i >= x)cnt[s[i-x]-'1']--; 26 if(cnt[0] && cnt[1] && cnt[2])return 1; 27 } 28 return 0; 29 } 30 int main(){ 31 // freopen("in.txt","r",stdin); 32 rd(T); 33 while(T--){ 34 scanf("%s",s);n=strlen(s); 35 if(n <= 2){ 36 printf("0\n"); 37 continue; 38 } 39 int l=3,r=n; 40 while(l != r){ 41 int mid=l+r>>1; 42 if(check(mid))r=mid; 43 else l=mid+1; 44 } 45 if(check(l))printf("%d\n",l); 46 else printf("0\n"); 47 } 48 return 0; 49 } 50 /**/
C1. Simple Polygon Embedding
题意:给出偶数n,求能将正2n边形包含在内的正方形的最小边长。
思路:这种题要大胆猜结论。想象不出来的话可以搜一搜对应多边形的图片辅助思考。可以发现正方形的边长恰好为2n边形对边相距的距离时,可以将其完全包含,而无论正2n边形如何旋转都不会使得答案变得更优。计算方面有很多方法,我选择的是余弦定理求出中心到端点距离,再用余弦定理求出对边距离。
1 #include<bits/stdc++.h> 2 #define LL long long 3 #define dl double 4 #define phi acos(-1) 5 void rd(int &x){ 6 x=0;int f=1;char ch=getchar(); 7 while(ch<'0' || ch>'9'){if(ch=='-')f=-1;ch=getchar();} 8 while(ch<='9' && ch>='0')x=x*10+ch-'0',ch=getchar();x*=f; 9 } 10 void lrd(LL &x){ 11 x=0;int f=1;char ch=getchar(); 12 while(ch<'0' || ch>'9'){if(ch=='-')f=-1;ch=getchar();} 13 while(ch<='9' && ch>='0')x=x*10+ch-'0',ch=getchar();x*=f; 14 } 15 const int INF=1e9; 16 const LL LINF=1e18; 17 using namespace std; 18 int T,n; 19 int main(){ 20 // freopen("in.txt","r",stdin); 21 rd(T); 22 while(T--){ 23 rd(n); 24 double tmp=1.0/(1-cos(phi/n)); 25 printf("%.9lf\n",sqrt(tmp*2-1)); 26 } 27 return 0; 28 } 29 /**/
C2. Not So Simple Polygon Embedding
题意:同C1,n为奇数。
思路:以正六边形为例,先尝试用C1的方法框住它。发现正方形边长为正六边形"对点"的距离,不妨设其为x,而正六边形对边到正方形的边之间还有一段空隙。我们旋转这个六边形。旋转角为a,可以发现它水平两点最远距离为xcosa,竖直两点最远距离为xcos(phi/2n - a),正方形的边长应当取二者之间的较大值,二者都是单调的,当其相等时他们的较大值最小,即a取phi/4n。至于对点的距离,可以用C1的办法求出对边距离,再用勾股定理。
1 #include<bits/stdc++.h> 2 #define LL long long 3 #define dl double 4 #define phi acos(-1) 5 void rd(int &x){ 6 x=0;int f=1;char ch=getchar(); 7 while(ch<'0' || ch>'9'){if(ch=='-')f=-1;ch=getchar();} 8 while(ch<='9' && ch>='0')x=x*10+ch-'0',ch=getchar();x*=f; 9 } 10 void lrd(LL &x){ 11 x=0;int f=1;char ch=getchar(); 12 while(ch<'0' || ch>'9'){if(ch=='-')f=-1;ch=getchar();} 13 while(ch<='9' && ch>='0')x=x*10+ch-'0',ch=getchar();x*=f; 14 } 15 const int INF=1e9; 16 const LL LINF=1e18; 17 using namespace std; 18 int T,n; 19 int main(){ 20 // freopen("in.txt","r",stdin); 21 rd(T); 22 while(T--){ 23 rd(n); 24 double tmp=1.0/(1-cos(phi/n)); 25 printf("%.9lf\n",sqrt(tmp*2)*cos(phi/n/4)); 26 } 27 return 0; 28 } 29 /**/
D. Multiset
题意:给出长度为n(1e6)的序列,q(1e6)次操作,每次插入一个数字或者删除第k小的数字,所有数字都在1~n之间,操作完成后输出任意一个存在于序列中的数字即可,没有则输出0。此题内存只有28MB。
思路:两个思路
思路一:树状数组+二分或者线段树模拟即可。内存没有问题,时间实际上差不多。
思路二:我们只需要输出一个数字,不妨尝试输出最小的数字。我们取定一个数字x,将序列中的数分为两种,<=x和>x,那么每次操作都是O1操作。在操作后<=x的数字是否存在这个方面,x有单调性,可以二分答案,最终可以得到最小的x使得经过所有操作之后仍存在<=x的数字。这个数字即最小数字。
1 #include<bits/stdc++.h> 2 #define LL long long 3 #define dl double 4 void rd(int &x){ 5 x=0;int f=1;char ch=getchar(); 6 while(ch<'0' || ch>'9'){if(ch=='-')f=-1;ch=getchar();} 7 while(ch<='9' && ch>='0')x=x*10+ch-'0',ch=getchar();x*=f; 8 } 9 void lrd(LL &x){ 10 x=0;int f=1;char ch=getchar(); 11 while(ch<'0' || ch>'9'){if(ch=='-')f=-1;ch=getchar();} 12 while(ch<='9' && ch>='0')x=x*10+ch-'0',ch=getchar();x*=f; 13 } 14 const int INF=1e9; 15 const LL LINF=1e18; 16 const int N=1e6+10; 17 using namespace std; 18 int n,q; 19 int a[N]; 20 int lowbit(int x){return x&(-x);} 21 void add(int x,int y){for(int i=x;i<=n;i+=lowbit(i))a[i]+=y;} 22 int sum(int x){int ans=0;for(int i=x;i;i-=lowbit(i))ans+=a[i];return ans;} 23 int main(){ 24 // freopen("in.txt","r",stdin); 25 rd(n);rd(q); 26 for(int i=1;i<=n;i++){ 27 int x;rd(x);add(x,1); 28 } 29 for(int i=1;i<=q;i++){ 30 int x;rd(x); 31 if(x >= 1)add(x,1); 32 else { 33 x=-x; 34 int l=1,r=n; 35 while(l != r){ 36 int mid=l+r>>1; 37 if(sum(mid) < x)l=mid+1; 38 else r=mid; 39 } 40 add(l,-1); 41 } 42 } 43 for(int i=1;i<=n;i++) 44 if(a[i]){printf("%d\n",i);exit(0);} 45 printf("0\n"); 46 return 0; 47 } 48 /**/
E. Graph Coloring
题意:n(5000)个点,m(1e5)条双向边,给每个点赋值1,2,3其中一个,使得任意相邻点差的绝对值恰好为1。并且最终1的个数为n1个,2的个数为n2个,3的个数为n3个。题目保证n1+n2+n3=n。输出赋值方案。无解输出NO。
思路:可以发现1和3其实是没有任何区别的,它们彼此不能"相连",也就是1和1,1和3,3和3,2和2这几种不能相连。所以把1和3看成一种情况,然后就是二分图黑白点染色的问题。如果出现了某个连通块不能染成二分图的模样,那么一定不合法。否则记录每个连通块黑白点的数目再进行类似于背包的dp。f[i][j]表示前i个连通块,j个填2的情况存不存在,转移的话只需要将当前连通块的黑点和白点分别填上2即可。输出方案时倒序枚举即可。需要注意数组越界的问题,比如f[i-1][n2-cnt[i][0]]就有可能越界,这也是我考试中没能通过此题的原因。
1 #include<bits/stdc++.h> 2 #define LL long long 3 #define dl double 4 void rd(int &x){ 5 x=0;int f=1;char ch=getchar(); 6 while(ch<'0' || ch>'9'){if(ch=='-')f=-1;ch=getchar();} 7 while(ch<='9' && ch>='0')x=x*10+ch-'0',ch=getchar();x*=f; 8 } 9 void lrd(LL &x){ 10 x=0;int f=1;char ch=getchar(); 11 while(ch<'0' || ch>'9'){if(ch=='-')f=-1;ch=getchar();} 12 while(ch<='9' && ch>='0')x=x*10+ch-'0',ch=getchar();x*=f; 13 } 14 const int INF=1e9; 15 const LL LINF=1e18; 16 const int N=1e5+10; 17 using namespace std; 18 int n,m,n1,n2,n3; 19 int fi[N],nxt[N<<1],to[N<<1],tot; 20 void link(int x,int y){nxt[++tot]=fi[x];fi[x]=tot;to[tot]=y;} 21 int col[N]; 22 int cnt[N][2]; 23 vector<int>S[N]; 24 void dfs(int x,int y){ 25 cnt[y][col[x]]++;S[y].push_back(x); 26 for(int i=fi[x];i;i=nxt[i]){ 27 if(col[to[i]] == col[x]){printf("NO\n");exit(0);} 28 else if(col[to[i]] == -1){col[to[i]]=col[x]^1;dfs(to[i],y);} 29 } 30 } 31 int ans[N]; 32 bool f[5005][5005]; 33 int main(){ 34 // freopen("in.txt","r",stdin); 35 rd(n);rd(m); 36 rd(n1);rd(n2);rd(n3); 37 for(int i=1;i<=m;i++){ 38 int x,y;rd(x);rd(y); 39 link(x,y);link(y,x); 40 } 41 memset(col,-1,sizeof(col)); 42 int now=0; 43 for(int i=1;i<=n;i++) 44 if(col[i] == -1){ 45 col[i]=0;dfs(i,++now); 46 } 47 f[0][0]=1; 48 for(int i=1;i<=now;i++){ 49 for(int j=0;j<=n;j++){ 50 if(!f[i-1][j])continue; 51 f[i][j+cnt[i][0]]=1; 52 f[i][j+cnt[i][1]]=1; 53 } 54 } 55 // for(int i=1;i<=now;i++){ 56 // for(int j=0;j<S[i].size();j++) 57 // cout<<S[i][j]<<" "<<col[S[i][j]]<<endl; 58 // puts(""); 59 // } 60 if(!f[now][n2])printf("NO\n"); 61 else { 62 for(int i=now;i>=1;i--){ 63 if(n2 >= cnt[i][0] && f[i-1][n2-cnt[i][0]]){ 64 int siz=S[i].size(); 65 for(int j=0;j<siz;j++) 66 if(!col[S[i][j]])ans[S[i][j]]=2; 67 else if(n1)ans[S[i][j]]=1,n1--; 68 else ans[S[i][j]]=3; 69 n2-=cnt[i][0]; 70 } 71 else { 72 int siz=S[i].size(); 73 for(int j=0;j<siz;j++) 74 if(col[S[i][j]])ans[S[i][j]]=2; 75 else if(n1)ans[S[i][j]]=1,n1--; 76 else ans[S[i][j]]=3; 77 n2-=cnt[i][1]; 78 } 79 } 80 printf("YES\n"); 81 for(int i=1;i<=n;i++)printf("%d",ans[i]); 82 puts(""); 83 } 84 return 0; 85 } 86 /**/
反思:所有用于数组下标并且可能出现负数的情况一定要注意判断防止越界。