牛客网NOIP赛前集训营-提高组(第八场)

时间:2023-03-08 22:02:14

染色

链接: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。

输入例子:
3
R
FL
RRRBR
FFFF
BRRBBRB
LFL
输出例子:
Yes
No
Yes

-->

示例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+);scanf("%s",opt+);
n=strlen(a+);m=strlen(opt+);
int r=,b=;
for(int i=; i<=n; i++)
{
if(a[i]!=a[i-])
{
if(a[i]=='R') r++;
else b++;
}
}
for(int i=m; i>=; i--)
{
if(opt[i]=='F')
{
r--;if(b>=) b--;
}
else
{
b--;
if(r>=) r--;
}
}
if(r<=&&b<=) puts("Yes");
else puts("No");
}
return ;
}

链接: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坐标。你可以发现只要知道这个值就能唯一确定矩形的位置。

输入例子:
3 1 5
0 0 1 1
1 0 2 1
4 0 5 1
输出例子:
5 6 7

-->

示例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[];
int tim[];
long long answer[];
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=; 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+,num+n+);
for(int i=; i<=n; i++) tim[i]=inf;
int start=;
while(num[start].pos!=t && start<=n) start++;
tim[start]=;
for(int i=start+; 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=; i<=n; i++) answer[num[i].pos]=num[i].x1+max(k-tim[i],);
for(int i=; 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<<]; inline void pushup(int cur)
{
tr[cur].mx=max(tr[ls].mx,tr[rs].mx);
} inline void pushdown(int cur)
{
if(tr[cur].lazy!=-)
{
tr[ls].mx=tr[cur].lazy,tr[rs].mx=tr[cur].lazy;
tr[ls].lazy=tr[rs].lazy=tr[cur].lazy;
tr[cur].lazy=-;
}
} 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)>>;
if(L<=mid) update(ls,l,mid,L,R,c);
if(mid<R) update(rs,mid+,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)>>,res=;
if(L<=mid) res=max(res,query(ls,l,mid,L,R));
if(mid<R) res=max(res,query(rs,mid+,r,L,R));
return res;
} int main()
{
scanf("%d%d%d",&n,&t,&k);
for(int i=; 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+,a+num+);
num=unique(a+,a+num+)-a-;
sort(m+,m+n+);
for(int i=; i<=n; i++)
{
ans[m[i].id]=m[i].x1;
if(m[i].id==t)
{
int x=lower_bound(a+,a+num+,m[i].y1)-a;
int y=lower_bound(a+,a+num+,m[i].y2)-a;
update(,,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+; i<=n; i++)
{
int x=lower_bound(a+,a+num+,m[i].y1)-a;
int y=lower_bound(a+,a+num+,m[i].y2)-a;
int pre=query(,,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(,,num,x,y,m[i].x2-m[i].x1+pre);
}
}
for(int i=;i<=n;i++) printf("%d ",ans[i]);
return ;
}

链接: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行。

输入例子:
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

-->

示例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=,f=;char c=getchar();
while(c>''||c<''){if(c=='-')f=-;c=getchar();}
while(c>=''&&c<=''){x=x*+c-'';c=getchar();}
return x*f;
} ll min(ll a,ll b){return a<b?a:b;} void solve1()
{
for(int i=;i<=n;i++) for(int j=;j<=n;j++) f[i][j]=-;
for(int i=;i<=n;i++) f[i][i]=a[i];
if(((n-)/k)%==) opt=;
else opt=; for(int L=k;L<n;L+=k)
{
if(L%k!=) continue;
opt^=;
for(int i=;i+L<=n;i++)
{
int j=i+L;ll res1=inf,res2=;
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=;x<k;x++)
{
if(i+x>j-k+x) break;
if(i+x>n || j-k+x<) continue;
if(f[i+x][j-k+x]==-) 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[][n]);
} int main()
{
freopen("data.txt","r",stdin);
freopen("2.out","w",stdout);
cin>>T;
while(T--)
{
ans=;
char ch[];scanf("%s",ch);
if(ch[]=='w')
{
n=read();k=read();
for(int i=;i<=n;i++) a[i]=read();
}
else
{
n=read();k=read();a[]=s=read();p=read();
for(int i=;i<=n;i++) a[i]=((*a[i-])%p+)%p;
}
if(n<= && k<=)
{
solve1();//solve2();
}
//else solve2();
cout<<ans<<endl;
}
return ;
}
/*
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[];
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[]=='w')
{
scanf("%d%d",&n,&k);
for(int i=;i<=n;++i) scanf("%lld", &num[i]);
}
else
{
scanf("%d%d%lld%lld",&n,&k,&S,&P);
num[]=S;
for(int i=;i<=n;++i) num[i]=Nxt(num[i-]);
}
Tim=(n-)/k;
for(int t=,d=Tim&;t<=Tim;++t,d=!d)
{
head=tail=;
memset(Q,,sizeof Q);
for(int i=,q=-k;i<=n;++i,++q)
{
while(head<tail && Q[head]<q) ++head;
while(head<tail && cmp(num[Q[tail-]], num[i], d)) --tail;
Q[tail++]=i;
if(q>) num[q]=num[Q[head]];
}
n-=k;
}
printf("%lld\n",num[]);
}
return ;
}