牛客网NOIP赛前集训营-提高组(第八场)
染色
链接:https://ac.nowcoder.com/acm/contest/176/A
来源:牛客网
时间限制:C/C++ 1秒,其他语言2秒
空间限制:C/C++ 524288K,其他语言1048576K
64bit IO Format: %lld
空间限制: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
说明
对第一个测试点,第二次操作可以是空的。
对第二个测试点,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
空间限制: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坐标。你可以发现只要知道这个值就能唯一确定矩形的位置。
/*
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]);
}
/*
正解有地方不太懂
时间比较紧了就这样吧...
*/
#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
空间限制: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。
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
说明
对于样例中第一个测试数据,初始有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
*/
/*
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\n",num[1]);
}
return 0;
}
折花枝,恨花枝,准拟花开人共卮,开时人去时。
怕相思,已相思,轮到相思没处辞,眉间露一丝。