zoukankan      html  css  js  c++  java
  • 牛客网NOIP赛前集训营-提高组(第八场)

    染色

    链接:https://ac.nowcoder.com/acm/contest/176/A
    来源:牛客网

    时间限制:C/C++ 1秒,其他语言2秒
    空间限制:C/C++ 524288K,其他语言1048576K
    64bit IO Format: %lld

    题目描述

    fizzydavid和leo有n个方格排成一排,每个方格初始是白色。fizzydavid有红色染料,leo有蓝色染料。他们共进行了m次操作,在每次操作中,fizzydavid或者leo会选择若干个(可以是零个)连续相邻的方格并用自己的染料给这些格子染色。当一个格子被染成某个颜色时,这种染料会覆盖之前这个格子上的颜色。

    现在你并不知道他们每次操作选择了哪些格子,只知道每次操作是谁进行的,以及最终这n个方格的颜色。你需要判断是否存在某种选择格子的方式使得操作完之后n个方格的颜色与给定的相同。你还发现,n个格子最终都不是白色。

    输入描述:

    第一行包含一个整数T,表示本组数据共有T组测试点。

    对每组测试点的第一行是一个由R和B组成的字符串s表示最终格子的颜色。R表示红色,B表示蓝色,同时字符串的长度为n。

    第二行是一个由F和L组成的字符串t表示m次操作,F表示某次是fizzydavid操作,L表示是leo操作,同时字符串的长度为m。

    所有数据满足T<=20。

    50%的数据满足n,m<=15

    80%的数据满足n,m<=100

    100%的数据满足n,m<=100000

    输出描述:

    对每组测试点输出一行,如果满足条件输出Yes否则输出No。
    示例1

    输入

    复制
    3
    R
    FL
    RRRBR
    FFFF
    BRRBBRB
    LFL

    输出

    复制
    Yes
    No
    Yes

    说明

    对第一个测试点,第二次操作可以是空的。

    对第二个测试点,s中有B,但是t中没有L,因此不合法。

    对第三个测试点,BBBBBBB -> BRRRRRB -> BRRBBRB就是合法的。

    /*
    正序涂色比较难操作,因为一个格子可以被染很多次。可以考虑倒序涂色
    会发现每个格子最多会被染色一次。
    因为后面的染色会覆盖前面的染色。
    所以可以染完色后把格子删掉。然后合并删掉两边的同色格子。 
    考虑贪心
    1.一定会删除尽量多的连续格子 2.一定会优先删中间的格子(删两边没有合并)。
    
    具体实现不必直接操作。只需要统计每个连续的同色的区域个数然后每次做减法就好。 
    最优操作和不优的操作在这里不会体现出来,这样可以默认是最优操作。 
    */
    #include<bits/stdc++.h>
    
    #define N 100007
    
    using namespace std;
    int n,m,T;
    char a[N],opt[N];
    
    int main()
    {
        scanf("%d",&T);
        while(T--)
        {
            scanf("%s",a+1);scanf("%s",opt+1);
            n=strlen(a+1);m=strlen(opt+1);
            int r=0,b=0;
            for(int i=1; i<=n; i++)
            {
                if(a[i]!=a[i-1])
                {
                    if(a[i]=='R') r++;
                    else b++;
                }
            }
            for(int i=m; i>=1; i--)
            {
                if(opt[i]=='F')
                {
                    r--;if(b>=2) b--;
                }
                else
                {
                    b--;
                    if(r>=2) r--;
                }
            }
            if(r<=0&&b<=0) puts("Yes");
            else puts("No");
        }
        return 0;
    }

     

    链接:https://ac.nowcoder.com/acm/contest/176/B
    来源:牛客网

    推箱子
    时间限制:C/C++ 2秒,其他语言4秒
    空间限制:C/C++ 524288K,其他语言1048576K
    64bit IO Format: %lld

    题目描述

    在平面上有n个箱子,每个箱子都可以看成一个矩形,两条边都和坐标轴平行。任何两个矩形都不相交,但可能有某个点或某条边重合。约定x轴正方向为右,y轴正方向为上。

    现在Fizzydavid要推这些箱子。他会选择某个箱子开始,并以每秒1个单位的速度使这个箱子向右移动。如果路上正面碰上某个箱子,被碰上的箱子会在碰到的那个瞬间开始进入运动状态,以1个单位的速度向右移动,不会转动或改变运动方向。

    准确地说,在某个时刻一个箱子i处于移动状态当且仅当:i是选择的箱子;或者存在一个处于移动状态的箱子j,它的右边界等于箱子i的左边界,且它们在y轴上的投影的公共长度>0。你可以发现在这种情况下,任意时刻每个矩形仍然不相交。

    Fizzydavid告诉了你所有的信息,需要你求出k秒后每个矩形的位置。

    输入描述:

    第一行两个整数n,t和k。Fizzydavid开始选择的是输入的第t个矩形。

    接下来n行每行四个整数x1,i,y1,i,x2,i,y2,i,表示矩形的左下角坐标是(x1,i,y1,i),右上角坐标是(x2,i,y2,i)。

    对于30%的数据,k<=100。

    对于另外40%的数据,n<=1000。

    对于所有的数据,n<=105,1<=t<=n,1<=k<=109,所有坐标都在-109和109之间。保证任意两个矩形不相交。

    输出描述:

    输出一行n个整数,第i个整数表示k秒后第i个矩形的左下角的x坐标。你可以发现只要知道这个值就能唯一确定矩形的位置。
    示例1

    输入

    复制
    3 1 5
    0 0 1 1
    1 0 2 1
    4 0 5 1

    输出

    复制
    5 6 7

    说明

    在刚开始时,前两个矩形就处于移动状态。在第二秒开始时,第三个矩形进入移动状态。
    /*
    copy
    发现矩形之间可以连边
    可以转化为类似最短路的问题。
    但只能过40
    发现有序处理矩形是不严格n^2的,竟然跑过70...     
    */
    #include<bits/stdc++.h> 
    #define int long long
    #define inf 66666666666ll
    #define max(a,b) (a>b?a:b)
    using namespace std;
    int n,t,k;
    struct node
    {
        long long x1,y1,x2,y2;
        int pos;
        bool operator <(const node a)const
        {
            if(x1!=a.x1) return x1<a.x1;
            return y1<a.y1;
        }
    } num[100010];
    int tim[100010];
    long long answer[100010];
    inline long long min(long long a,long long b)
    {
        return a<b?a:b;
    }
    signed main()
    {
        scanf("%lld%lld%lld",&n,&t,&k);
        for(int i=1; i<=n; i++) scanf("%lld%lld%lld%lld",&num[i].x1,&num[i].y1,&num[i].x2,&num[i].y2),num[i].pos=i;
        sort(num+1,num+n+1);
        for(int i=1; i<=n; i++) tim[i]=inf;
        int start=1;
        while(num[start].pos!=t && start<=n) start++;
        tim[start]=0;
        for(int i=start+1; i<=n; i++)
        {
            int ans=inf;
            for(int j=start; j<i; j++)
            {
                if((num[j].y1>=num[i].y1 && num[j].y1<num[i].y2) || (num[j].y1<=num[i].y1 && num[j].y2>num[i].y1))
                    ans=min(ans,tim[j]+num[i].x1-num[j].x2);
            }
            tim[i]=ans;
            if(ans<inf && ans>k) break;
        }
        for(int i=1; i<=n; i++) answer[num[i].pos]=num[i].x1+max(k-tim[i],0);
        for(int i=1; i<=n; i++)
            printf("%lld ",answer[i]);
    }
    70暴力
    /*
    正解有地方不太懂
    时间比较紧了就这样吧... 
    */
    #include<bits/stdc++.h>
    
    #define N 100007
    
    #define ls cur<<1
    #define rs cur<<1|1
    
    using namespace std;
    int n,t,k,a[N],num,pre[N],ans[N];
    struct qwq{
        int x1,y1,x2,y2,id;
        bool operator <(const qwq &x) const{
            return x1<x.x1||(x1==x.x1&&y1<x.y1);
        }
    } m[N];
    struct Node{
        int mx,lazy;
    } tr[N<<2];
    
    inline void pushup(int cur)
    {
        tr[cur].mx=max(tr[ls].mx,tr[rs].mx);
    }
    
    inline void pushdown(int cur)
    {
        if(tr[cur].lazy!=-1)
        {
            tr[ls].mx=tr[cur].lazy,tr[rs].mx=tr[cur].lazy;
            tr[ls].lazy=tr[rs].lazy=tr[cur].lazy;
            tr[cur].lazy=-1;
        }
    }
    
    inline void update(int cur,int l,int r,int L,int R,int c)
    {
        if(L<=l && r<=R)
        {
            tr[cur].lazy=tr[cur].mx=c;
            return;
        }
        pushdown(cur);
        int mid=(l+r)>>1;
        if(L<=mid) update(ls,l,mid,L,R,c);
        if(mid<R) update(rs,mid+1,r,L,R,c);
        pushup(cur);
    }
    
    inline int query(int cur,int l,int r,int L,int R)
    {
        if(L<=l && r<=R) return tr[cur].mx;
        pushdown(cur);
        int mid=(l+r)>>1,res=0;
        if(L<=mid) res=max(res,query(ls,l,mid,L,R));
        if(mid<R) res=max(res,query(rs,mid+1,r,L,R));
        return res;
    }
    
    int main()
    {
        scanf("%d%d%d",&n,&t,&k);
        for(int i=1; i<=n; i++)
        {
            scanf("%d%d%d%d",&m[i].x1,&m[i].y1,&m[i].x2,&m[i].y2);
            m[i].id=i;
            a[++num]=m[i].y1; a[++num]=m[i].y2;
        }
        sort(a+1,a+num+1);
        num=unique(a+1,a+num+1)-a-1;
        sort(m+1,m+n+1);
        for(int i=1; i<=n; i++)
        { 
            ans[m[i].id]=m[i].x1;
            if(m[i].id==t)
            {
                int x=lower_bound(a+1,a+num+1,m[i].y1)-a;
                int y=lower_bound(a+1,a+num+1,m[i].y2)-a;
                update(1,1,num,x,y,m[i].x2-m[i].x1);
                t=i;
                break;
            }
        }
        k+=m[t].x1;
        ans[m[t].id]=k;
        for(int i=t+1; i<=n; i++)
        {
            int x=lower_bound(a+1,a+num+1,m[i].y1)-a;
            int y=lower_bound(a+1,a+num+1,m[i].y2)-a;
            int pre=query(1,1,num,x,y);
            if(!pre || pre+k<=m[i].x1) ans[m[i].id]=m[i].x1;
            else
            {
                ans[m[i].id]=k+pre;
                update(1,1,num,x,y,m[i].x2-m[i].x1+pre);
            }
        }
        for(int i=1;i<=n;i++) printf("%d ",ans[i]);
        return 0;
    }

     

    链接:https://ac.nowcoder.com/acm/contest/176/C
    来源:牛客网

    Revue
    时间限制:C/C++ 1秒,其他语言2秒
    空间限制:C/C++ 524288K,其他语言1048576K
    64bit IO Format: %lld

    题目描述

    梦想的revue拉开帷幕,你需要和你的对手争夺position zero。revue的形式是一个游戏,游戏中有一排共n个位置可以争夺,每个位置有一个权值,第i个位置的权值为ai。游戏是回合制的,你和你的对手轮流进行操作,你是先手。每一回合,进行操作的玩家需要在这一排位置的两端一共标记K个位置,一个位置最多只能被标记一次,操作必须保证每时每刻未被标记的位置是连续的。最终剩下的一个未标记的位置的权值就是游戏的得分,你需要使这个得分最大,你的对手需要使这个得分最少。保证最后只剩下一个位置未被标记,即n-1是K的倍数。若你和你的对手都使用最优策略进行操作,请问这个游戏的最终得分是多少。

    输入描述:

    本题有多组测试数据。第一行一个整数T表示测试数据组数。接下来分别是T组数据。每组数据内:
    为了避免输入过慢,输入数据分为两种。若第一行为raw,则表示这组数据是输入给定的。第二行会有两个整数n和K。第三行有n个整数,第i个数表示ai。若第一行为random,则表示这组数据是随机生成的。第二行会有4个整数n,K,S,P。表示a1=S, ai= (2333ai-1+6666) mod P。(mod表示取模操作)
    20%的数据 1 ≤ n,K ≤ 10
    40%的数据保证 1 ≤ n,K ≤ 1000
    另外20%的数据保证 1 ≤ n ≤ 10000, K ≥ 100
    80%的数据保证 1 ≤ n,K ≤ 10000
    100%的数据保证n-1是K的倍数,0 ≤ T ≤ 100,1 ≤ K < n ≤ 100000,0 ≤ S, P, ai ≤ 109,类型为raw的数据的n的和不超过500000。

    输出描述:

    每组数据输出一行表示答案,一共输出T行。
    示例1

    输入

    复制
    3
    raw
    5 2
    5 3 4 2 1
    raw
    7 2
    5 3 4 2 1 6 7
    random
    13 3 2 20

    输出

    复制
    3
    4
    2

    说明

    对于样例中第一个测试数据,初始有5个位置未标记,他们的权值是:

    5 3 4 2 1

    第一回合,最优策略下你选择标记右端的两个位置(即第4个位置和第5个位置),剩下来未被标记的位置的权值为:

    5 3 4

    第二回合,最优策略下你的对手选择标记左端的一个位置和右端的一个位置(即第1个位置和第3个位置),剩下来未被标记的位置的权值为:

    3

    这个游戏的最终得分为3。注意当你和你的对手都是用最优策略进行操作时,游戏最终的得分是唯一的。

    #include<bits/stdc++.h>
    
    #define N 5001
    #define M 100007
    #define ll long long
    #define inf 0x3f3f3f3f
    
    using namespace std;
    ll n,m,k,s,p,ans,cnt,T;
    ll f[N][N],a[M],sum[M];
    bool opt;
    
    inline int read()
    {
        int x=0,f=1;char c=getchar();
        while(c>'9'||c<'0'){if(c=='-')f=-1;c=getchar();}
        while(c>='0'&&c<='9'){x=x*10+c-'0';c=getchar();}
        return x*f;
    }
    
    ll min(ll a,ll b){return a<b?a:b;}
    
    void solve1()
    {
            for(int i=1;i<=n;i++) for(int j=1;j<=n;j++) f[i][j]=-1;
            for(int i=1;i<=n;i++) f[i][i]=a[i];
            if(((n-1)/k)%2==0) opt=0;
            else opt=1;
            
            for(int L=k;L<n;L+=k) 
            {
                if(L%k!=0) continue;
                opt^=1;
                for(int i=1;i+L<=n;i++)
                {
                    int j=i+L;ll res1=inf,res2=0;
                    res1=min(res1,f[i+k][j]); res2=max(res2,f[i+k][j]);
                    res1=min(res1,f[i][j-k]); res2=max(res2,f[i][j-k]);
                    for(int x=1;x<k;x++)
                    {
                        if(i+x>j-k+x) break;
                        if(i+x>n || j-k+x<0) continue;
                        if(f[i+x][j-k+x]==-1) continue;
                        res1=min(res1,f[i+x][j-k+x]); res2=max(res2,f[i+x][j-k+x]);        
                    }
                    if(opt) f[i][j]=res1;
                    else f[i][j]=res2;
                }
            }
            ans=max(ans,f[1][n]);
    }
    
    int main()
    {
        freopen("data.txt","r",stdin);
        freopen("2.out","w",stdout);
        cin>>T;
        while(T--)
        {
            ans=0;
            char ch[5];scanf("%s",ch);
            if(ch[2]=='w')
            {
                n=read();k=read();
                for(int i=1;i<=n;i++) a[i]=read();
            }
            else
            {
                n=read();k=read();a[1]=s=read();p=read();
                for(int i=2;i<=n;i++) a[i]=((2333*a[i-1])%p+6666)%p;
            }
            if(n<=5000 && k<=5000) 
            {
                solve1();//solve2();
            }
            //else solve2();
            cout<<ans<<endl;
        }
        return 0;
    }
    /*
    1
    raw
    7 2
    1 1 9 2 2 2 2
    
    ans=9
    */
    40暴力
    /*
    60暴力 
    改变一下40暴力dp状态,定义f[i]为以i开头的长度为x*k+1的dp值
    这样f[i]就可以对每次操作用单调队列维护答案了。 
    */
    #include<bits/stdc++.h> 
    
    #define N 100007
    #define ll long long
    
    char ch[111];
    int T,n,k,Tim,head,tail,S,P;
    ll num[N],Q[N];;
    
    ll Nxt(ll v){return (2333LL*v+6666LL)%P;}
    
    bool cmp(ll a, ll b, int d)
    {
        if(d) return a<=b;
        return a>=b;
    }
    
    int main()
    {
        scanf("%d",&T);
        while(T--)
        {
            scanf("%s",ch);
            if(ch[2]=='w')
            {
                scanf("%d%d",&n,&k);
                for(int i=1;i<=n;++i) scanf("%lld", &num[i]);
            }
            else
            {
                scanf("%d%d%lld%lld",&n,&k,&S,&P);
                num[1]=S;
                for(int i=2;i<=n;++i)   num[i]=Nxt(num[i-1]);
            }
            Tim=(n-1)/k;
            for(int t=1,d=Tim&1;t<=Tim;++t,d=!d)
            {
                head=tail=0;
                memset(Q,0,sizeof Q);
                for(int i=1,q=1-k;i<=n;++i,++q)
                {
                    while(head<tail && Q[head]<q) ++head;
                    while(head<tail && cmp(num[Q[tail-1]], num[i], d))   --tail;
                    Q[tail++]=i;
                    if(q>0)  num[q]=num[Q[head]];
                }
                n-=k;
            }
            printf("%lld
    ",num[1]);
        }
        return 0;
    }

     

     



  • 相关阅读:
    关于lockkeyword
    关于多层for循环迭代的效率优化问题
    Android 面试精华题目总结
    Linux基础回想(1)——Linux系统概述
    linux源代码编译安装OpenCV
    校赛热身 Problem C. Sometimes Naive (状压dp)
    校赛热身 Problem C. Sometimes Naive (状压dp)
    校赛热身 Problem B. Matrix Fast Power
    校赛热身 Problem B. Matrix Fast Power
    集合的划分(递推)
  • 原文地址:https://www.cnblogs.com/L-Memory/p/9905358.html
Copyright © 2011-2022 走看看