Codeforces 部分题目题解(口胡)
来源:互联网 发布:python ctypes 编辑:程序博客网 时间:2024/05/19 10:40
883D
题面
题目大意:给你一个长度为n的字符串,上面有牛(“P”),草(“*”)和空地(“.”)。现在你给每一头牛规定一个方向,它会一直往前吃草,直到走到边界。每一份草只会被吃1次,要求输出最多吃多少草,以及在此基础下吃完最后一份草的最小时间。n<=1000000。
做法:很明显两头牛就可以吃完所有草,于是暴力处理0,1头牛的情况。然后由于具有单调性,考虑二分答案后贪心(时限3s不虚)。接下来证明两个小结论:
1.最前面的草,顶多会被它后面第二头牛吃掉。
这个从上图就可以看出。①中红色和蓝色的边分别比②中红色和蓝色的边长。
2.二分完阀值mid后,如果最前面的草后面mid处至少有两头牛,且第一头牛与第二头牛之间没有草,那么让第一头牛吃该草,第二头牛往后吃。
这个结论应该很显然……
但是如果两头牛中间有草怎么办呢?是不是让第二头牛往前吃,第一头牛往后吃就最优了?答案是否定的。我一开始按照这个思路写了个贪心,结果被下面这组数据卡掉了:
19
(这组数据的最优方案应该是让第一,三头牛往前吃,第二头牛往后吃)
所以我们要用DP!记f[i]=j表示考虑完第i个人之后,最多能处理完1~j处的草。然后根据上述思路用f[i-2]或f[i-1]转移即可。 我一开始把时限看成了1s,结果想了很久都想不出O(n)的做法QAQ
CODE:
#include<iostream>#include<string>#include<cstring>#include<cmath>#include<cstdio>#include<cstdlib>#include<stdio.h>#include<algorithm>using namespace std;const int maxn=1000100;int sum[maxn];int f[maxn];int id[maxn];char s[maxn];int a[maxn];int n;int cnt=0,num=0;bool Judge(int x){ f[0]=0; for (int i=1; i<=num; i++) { f[i]=0; int y=id[i]; if (f[i-1]>=y-1) f[i]=max(f[i],y+x); if (f[i-1]<y-1) if (sum[y]-sum[ f[i-1] ]) if (sum[max(0,y-x-1)]-sum[ f[i-1] ]>0) return false; else { f[i]=max(f[i],y); if ( i>=2 && sum[max(0,y-x-1)]-sum[ f[i-2] ]<=0 ) f[i]=max(f[i],id[i-1]+x); } else f[i]=max(f[i],y+x); } if ( f[num]>=n || sum[n]-sum[ f[num] ]<=0 ) return true; return false;}int Binary(){ int L=0,R=n; while (L+1<R) { int mid=(L+R)>>1; if ( Judge(mid) ) R=mid; else L=mid; } return R;}int main(){ freopen("2326.in","r",stdin); freopen("2326.out","w",stdout); scanf("%d",&n); scanf("%s",s); for (int i=1; i<=n; i++) { if (s[i-1]=='*') a[i]=0,cnt++; if (s[i-1]=='P') a[i]=1,id[++num]=i; if (s[i-1]=='.') a[i]=2; } if (num<=1) if (num==0) printf("0 0\n"); else { int x=0,y=0; for (int i=1; i<=n; i++) if (a[i]==1) x=i; for (int i=1; i<=x; i++) if (a[i]==0) y++; int Le=0,Ri=0; for (int i=x; i>=1; i--) if (a[i]==0) Le=x-i; for (int i=x; i<=n; i++) if (a[i]==0) Ri=i-x; if (y>cnt-y) printf("%d %d\n",y,Le); else if (y<cnt-y) printf("%d %d\n",cnt-y,Ri); else if (Le<Ri) printf("%d %d\n",y,Le); else printf("%d %d\n",cnt-y,Ri); } else { sum[0]=0; for (int i=1; i<=n; i++) { sum[i]=sum[i-1]; if (a[i]==0) sum[i]++; } int ans=Binary(); printf("%d %d\n",cnt,ans); } return 0;}
875E
题面
题目大意:有n个城市,给出它们在数轴上的坐标。现在有两个人在s1和s2处,他们要按顺序走完这n个城市,求他们两个人最大距离的最小值。n<=100000。
做法:分析之后发现,它就是要你把这n个城市分成若干段,使得每一段的所有城市到上一段的最后一个城市的距离小于等于ans。二分答案之后用treap维护即可,时间复杂度
然而这样做会被卡常(虽然CF的机子跑得灰常快)。
更优的方法是从后往前考虑。如果第n-1个城市在[a[n]-mid,a[n]+mid]的范围内,就可以无视掉第n个城市;否则就要求第n-2个城市在[a[n]-mid,a[n]+mid]与[a[n-1]-mid,a[n-1]+mid]的交集内,不存在则无解。这样时间就是
CODE(Treap):
#include<iostream>#include<string>#include<cstring>#include<cmath>#include<cstdio>#include<cstdlib>#include<stdio.h>#include<algorithm>using namespace std;const int maxn=100100;const int oo=2000001000;const long long M1=998244353;const long long M2=1000000007;const long long M3=1333333331;typedef long long LL;LL seed;struct Tnode{ int val,id,min_id,fix; Tnode *lson,*rson;} tree[maxn];Tnode *Root;int cur;int Right[maxn];int a[maxn];int n,s1,s2;int Rand(){ seed=(seed*M1+M2)%M3; return seed;}Tnode *New_node(int Val,int Id){ cur++; tree[cur].val=Val; tree[cur].min_id=tree[cur].id=Id; tree[cur].fix=Rand(); tree[cur].lson=tree[cur].rson=NULL; return tree+cur;}void Recount(Tnode *P){ P->min_id=P->id; if (P->lson) P->min_id=min(P->min_id,P->lson->min_id); if (P->rson) P->min_id=min(P->min_id,P->rson->min_id);}void Right_turn(Tnode *&P){ Tnode *W=P->lson; P->lson=W->rson; W->rson=P; P=W; Recount(P->rson); Recount(P);}void Left_turn(Tnode *&P){ Tnode *W=P->rson; P->rson=W->lson; W->lson=P; P=W; Recount(P->lson); Recount(P);}void Insert(Tnode *&P,int Val,int Id){ if (!P) P=New_node(Val,Id); else if ( Val<P->val || ( Val==P->val && Id<P->id ) ) { Insert(P->lson,Val,Id); if ( P->lson->fix < P->fix ) Right_turn(P); else Recount(P); } else { Insert(P->rson,Val,Id); if ( P->rson->fix < P->fix ) Left_turn(P); else Recount(P); }}int Find_succ(Tnode *P,int Val,int Ans){ if (!P) return Ans; if (Val<P->val) { Ans=min(Ans,P->id); if (P->rson) Ans=min(Ans,P->rson->min_id); return Find_succ(P->lson,Val,Ans); } return Find_succ(P->rson,Val,Ans);}int Find_prev(Tnode *P,int Val,int Ans){ if (!P) return Ans; if (P->val<Val) { Ans=min(Ans,P->id); if (P->lson) Ans=min(Ans,P->lson->min_id); return Find_prev(P->rson,Val,Ans); } return Find_prev(P->lson,Val,Ans);}bool Judge(int x){ Root=NULL; cur=-1; Insert(Root,-oo,n+1); Insert(Root,oo,n+1); for (int i=n; i>=1; i--) { int Succ=Find_succ(Root,a[i]+x,n+1); int Prev=Find_prev(Root,a[i]-x,n+1); Right[i]=min(Prev,Succ)-1; Insert(Root,a[i],i); } int now=0; int Succ=Find_succ(Root,s1+x,n+1); int Prev=Find_prev(Root,s1-x,n+1); now=max(now, min(Prev,Succ)-1 ); Succ=Find_succ(Root,s2+x,n+1); Prev=Find_prev(Root,s2-x,n+1); now=max(now, min(Prev,Succ)-1 ); for (int i=1; i<=n; i++) { if (i>now) return false; now=max(now,Right[i]); } return true;}int Binary(){ int L=0,R=1000000000; while (L+1<R) { int mid=(L+R)>>1; if ( Judge(mid) ) R=mid; else L=mid; } return R;}int main(){ freopen("2330.in","r",stdin); freopen("2330.out","w",stdout); scanf("%d%d%d",&n,&s1,&s2); seed=n; for (int i=1; i<=n; i++) scanf("%d",&a[i]); int ans=Binary(); ans=max(ans, max(s1-s2,s2-s1) ); printf("%d\n",ans); return 0;}
CODE(区间交):
#include<iostream>#include<string>#include<cstring>#include<cmath>#include<cstdio>#include<cstdlib>#include<stdio.h>#include<algorithm>using namespace std;const int maxn=100100;int a[maxn];int n,s1,s2;bool Judge(int x){ int L=a[n]-x,R=a[n]+x; for (int i=n-1; i>=1; i--) { if ( L<=a[i] && a[i]<=R ) L=a[i]-x,R=a[i]+x; else { L=max(L,a[i]-x); R=min(R,a[i]+x); if (L>R) return false; } } if ( L<=s1 && s1<=R ) return true; if ( L<=s2 && s2<=R ) return true; return false;}int Binary(){ int L=0,R=1000000000; while (L+1<R) { int mid=(L+R)>>1; if ( Judge(mid) ) R=mid; else L=mid; } return R;}int main(){ freopen("2330.in","r",stdin); freopen("2330.out","w",stdout); scanf("%d%d%d",&n,&s1,&s2); for (int i=1; i<=n; i++) scanf("%d",&a[i]); int ans=Binary(); ans=max(ans, max(s1-s2,s2-s1) ); printf("%d\n",ans); return 0;}
875F
题面
题目大意:给出m个三元组(a,b,c),表示如果该组选择了a或b两个数中的一个,你就会获得c的报酬。每个数顶多属于一个组,每个组顶多选择一个数。要求最大化报酬和。a,b<=n,n,m<=200000。
做法:这就是道典型的二分图匹配嘛
将所有三元组按c从大到小排序,然后按顺序处理。对于第i个三元组,先查看ai和bi是否在同一个集合,是的话再看这个集合是否已经有一个环,有环则选不了;不在一个集合,就看两个集合是否都有环,都有环则选不了,否则获得c的贡献,然后合并ai和bi所在集合。时间复杂度为
(代码因特殊原因不贴出)。 lhxQAQ老贼丧天良,我与珂朵莉共存亡!!!
(持续待更)
- Codeforces 部分题目题解(口胡)
- PythonTip 50道题目(部分题解)
- Codeforces Good Bye 2016(部分题解)
- Codeforces Round332 部分题解
- CodeForces 22、23部分题解
- Codeforces Round #418部分题解
- Regionals 2013 Asia - Daejeon (部分题目题解)
- Codeforces Educational Codeforces Round 16 部分题解
- Codeforces Round #227 (Div. 2)(部分题解)
- codeforces Round 213 div2部分题解
- Codeforces Round #216 (Div. 2)部分题解
- Codeforces Round #219div1 部分题解
- Codeforces Round #277.5 (Div. 2)部分题解
- Codeforces Good Bye 2014 部分题解
- codeforces #549 Looksery Cup 部分题解
- Codeforces Round #359 (Div. 2) 部分题解
- Codeforces Round #378 (Div. 2) 部分题解
- Codeforces Round #386 (Div.2)部分题解
- StarWars.Android碎片式过渡动画
- Capstone训练营11
- IT与风投
- 操作系统知识结构图
- Qt抽奖圆盘(减速效果)
- Codeforces 部分题目题解(口胡)
- POJ 2253 Frogger
- 工作笔记之MFC属性页
- git 常用操作
- RESTful 简介
- bzoj 5097: 实时导航
- 机器学习&数据挖掘笔记_16(常见面试之机器学习算法思想简单梳理)
- 支付宝SDK集成服务端(java)
- MySQL经常出现sleep进程的解决办法