2018.11.4牛客noip提高组模拟赛(第八场)

A-染色

题在这
把操作序列倒过来考虑,每次染色只会染一个极长连续段,染中间就可以删掉将两边的合并,所以把连续的都看成一个,如果能删中间就删中间,长度 − 2 -2 2,不能删中间就删两边。需要记一下开头那个是什么判断一下合法就好了。

#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cstring>
#include<cmath>
#define maxn 100005
using namespace std;
int t,n,m,cnt,fr;
char s[maxn],a[maxn];

int main(){
	scanf("%d",&t);
	while(t--){
		scanf("%s%s",s+1,a+1); n=strlen(s+1); m=strlen(a+1);
		cnt=0; fr=s[1]=='R';
		for(int i=1;i<=n;i++)
			if(s[i]!=s[i-1]) cnt++;
		for(int i=m;i;i--){
			int now=a[i]=='F';
			if(cnt>=4) cnt-=2;
			else if(cnt==3 && (fr^now)) cnt-=2;
			else if(cnt==3) cnt--;
			else if(cnt==2) {
				cnt--;
				if(now==fr) fr^=1;
			}
			else if(cnt==1 && now==fr) cnt--;
			if(!cnt) break;
		}
		if(!cnt) puts("Yes");
		else puts("No");
	}
	return 0;
}

考场上是正着做然后每次删两边的,又复杂还不知道对不对,还是倒着想比较好啊 q w q qwq qwq

B-推箱子

题面
啊这个出题人是平衡树或者 s e t set set + d i j k s t r a +dijkstra +dijkstra这样讲的
我觉得线段树就又好理解又好写啊
(虽然 p u s h d o w n pushdown pushdown手残写错还没有对拍挂的惨惨的···
具体就是按 x x x坐标排个序,然后离散化一下 y y y坐标,找到每个箱子往左第一个会被哪个箱子碰到,然后更新一下答案就好了

#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cstring>
#include<cmath>
#include<map>
#define maxn 200005
#define ls cur<<1
#define rs cur<<1|1
#define inf 0x3f3f3f3f
using namespace std;
int n,t,k,a[maxn],num,pre[maxn],ans[maxn];

inline int rd(){
	int x=0,f=1;char c=' ';
	while(c<'0' || c>'9') f=c=='-'?-1:1,c=getchar();
	while(c<='9' && c>='0') x=x*10+c-'0',c=getchar();
	return x*f;
}
inline int min(int x,int y){return x<y?x:y;}
inline int max(int x,int y){return x>y?x:y;}

struct qwq{
	int x1,y1,x2,y2,id;
	bool operator <(const qwq &x) const{
		return x1<x.x1||(x1==x.x1&&y1<x.y1);
	}
}tr[maxn];
inline bool cmp(qwq x,qwq y){return x.id<y.id;}

struct Node{
	int mx,lazy; Node(){mx=lazy=-1;}
}node[maxn<<2];

inline void pushup(int cur){
	node[cur].mx=max(node[ls].mx,node[rs].mx);
}
inline void pushdown(int cur){
	if(node[cur].lazy!=-1){
		node[ls].mx=node[cur].lazy,node[rs].mx=node[cur].lazy;//就是这的cur写成rs了
		node[ls].lazy=node[rs].lazy=node[cur].lazy;
		node[cur].lazy=-1;
	}
}

inline void update(int cur,int l,int r,int L,int R,int c){
	if(L<=l && r<=R) {
		node[cur].lazy=node[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 node[cur].mx;
	pushdown(cur);
	int mid=(l+r)>>1,res=-1;
	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(){
	n=rd(); t=rd(); k=rd();
	for(int i=1;i<=n;i++){
		tr[i].x1=rd(),tr[i].y1=rd(),tr[i].x2=rd(),tr[i].y2=rd();
		tr[i].id=i; a[++num]=tr[i].y1,a[++num]=tr[i].y2;
	}
	sort(a+1,a+num+1);
	num=unique(a+1,a+num+1)-a-1;
	sort(tr+1,tr+n+1);
	for(int i=1;i<=n;i++){
		ans[tr[i].id]=tr[i].x1;
		if(tr[i].id==t){
			int x=lower_bound(a+1,a+num+1,tr[i].y1)-a;
			int y=lower_bound(a+1,a+num+1,tr[i].y2)-a;
			update(1,1,num,x,y,tr[i].x2-tr[i].x1);
			t=i; break;
		}
	}
	k+=tr[t].x1; ans[tr[t].id]=k;
	for(int i=t+1;i<=n;i++){
		int x=lower_bound(a+1,a+num+1,tr[i].y1)-a;
		int y=lower_bound(a+1,a+num+1,tr[i].y2)-a;
		int pre=query(1,1,num,x,y);
		if(pre==-1 || pre+k<=tr[i].x1) ans[tr[i].id]=tr[i].x1;
		else{
			ans[tr[i].id]=k+pre;
			update(1,1,num,x,y,tr[i].x2-tr[i].x1+pre);
		}
	}
	for(int i=1;i<=n;i++) printf("%d ",ans[i]);
	return 0;
}

T3-Revue

毒瘤题
博弈论,区间 d p dp dp的思想,还需要一堆优化还要证明一堆东西太麻烦不改了

把出题人的 p p t ppt ppt放过来

首先是部分分

由于任意时刻未被标记的格子一定是一个连续的区间,并且如果知道了这个区间就可以推算出先后手是谁。可以考虑使用区间dp在O(n^2)的时间内解决问题:
dp[l][r]表示若未被标记的区间为[l,r],那么结束时游戏的得分是多少。
若是你的回合,那么dp[l][r]=max{dp[l+k][r], dp[l+k-1][r-1],…,dp[l][r-k]}
若是对手的回合,那么dp[l][r]=min{dp[l+k][r], dp[l+k-1][r-1],…,dp[l][r-k]}
注意到转移是O(K)的,状态数为O(N^2/K) (因为只有长度减一为K的倍数时才是一个合法的状态)
转移若使用单调队列,可以优化到O(1),可以通过第三个子任务。

考虑二分答案游戏的得分是否能大于等于X。此时可以将游戏的目标改为,若最后的得分大于等于X则你胜,否则对手胜。于是序列中的权值可以改为0或1,0表示小于X,1表示大于等于X,这样我们只要判断你是否有策略可以使游戏最终得分为1。使用同样的dp。

(据说二分能卡过

下面是正解
用另外一种记法:dp[i][j]表示长度为iK+1,起始位置为j的dp值,这样dp[i]就从dp[i-1]转移过来了。
考虑n-1是2K的倍数的时候。最后一步是后手走的。
打表发现当i大的时候 将dp值排成倒三角形式 dp[i]交替出现
举个栗子 设K=1:
dp[0]= 1 0 1 1 0 1 0 1 1 0 1 1 0 0 0dp[1]= 0 0 1 0 0 0 0 1 0 0 1 0 0 0 min
dp[2]= 0 1 1 0 0 0 1 1 0 1 1 0 0 max
dp[3]= 0 1 0 0 0 0 1 0 0 1 0 0 min
dp[4]= 1 1 0 0 0 1 1 0 1 1 0 max
dp[5]= 1 0 0 0 0 1 0 0 1 0 min
dp[6]= 1 0 0 0 1 1 0 1 1 max
dp[7]= 0 0 0 0 1 0 0 1 min
当i>2时dp[i]在有值的地方与dp[i-2]相同 所以我们只需要算出dp[1]与dp[2]就可以知道所有dp值 dp[(n-1)/K][1]=dp[2][(n+1)/2-K]
(n-1)/K为奇数时也类似。时间复杂度O(n log n)。

考虑我们之前的结论。
dp[2n][1]=dp[2][(n+1)/2-K]
说明n-1是2K的倍数的时候,这个游戏的得分与中间2K+1个值组成的游戏的得分一样。
这时我们可以舍弃二分,使用单调队列优化的dp,可以做到O(n)。
若(n-1)/K为奇数,可以枚举第一步先手怎么走的,问题就转化成了n-1是2K的倍数的情况。同样可以使用单调队列优化的dp做到O(n)。也可以用若(n-1)/K为奇数,这个游戏的得分与中间K+1个值组成的游戏的得分一样。

总复杂度O(K)。

n-1是2K的倍数的时候,这个游戏的得分与中间2K+1个值组成的游戏的得分一样。这个结论也可以通过别的途径来证明。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值