Codeforces 部分题目题解(口胡)

来源:互联网 发布:python ctypes 编辑:程序博客网 时间:2024/05/19 10:40

883D

题面

题目大意:给你一个长度为n的字符串,上面有牛(“P”),草(“*”)和空地(“.”)。现在你给每一头牛规定一个方向,它会一直往前吃草,直到走到边界。每一份草只会被吃1次,要求输出最多吃多少草,以及在此基础下吃完最后一份草的最小时间。n<=1000000。

做法:很明显两头牛就可以吃完所有草,于是暴力处理0,1头牛的情况。然后由于具有单调性,考虑二分答案后贪心(时限3s不虚)。接下来证明两个小结论:

1.最前面的草,顶多会被它后面第二头牛吃掉。
这个从上图就可以看出。①中红色和蓝色的边分别比②中红色和蓝色的边长。

2.二分完阀值mid后,如果最前面的草后面mid处至少有两头牛,且第一头牛与第二头牛之间没有草,那么让第一头牛吃该草,第二头牛往后吃。
这个结论应该很显然……
但是如果两头牛中间有草怎么办呢?是不是让第二头牛往前吃,第一头牛往后吃就最优了?答案是否定的。我一开始按照这个思路写了个贪心,结果被下面这组数据卡掉了:
19
P.....P...P
(这组数据的最优方案应该是让第一,三头牛往前吃,第二头牛往后吃)
所以我们要用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维护即可,时间复杂度O(nlog2(n))
然而这样做会被卡常(虽然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]的交集内,不存在则无解。这样时间就是O(nlog(n))

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所在集合。时间复杂度为O(nα(n))

(代码因特殊原因不贴出)。
lhxQAQ老贼丧天良,我与珂朵莉共存亡!!!


(持续待更)

原创粉丝点击