Codeforces Educational Codeforces Round 118 (A-E)部分

A. Long Comparison

题意:给出两个数字,都表示为 x + 后 缀 有 p 个 0 x+后缀有p个0 x+p0.比较两个数字的大小
思路:一开始是傻傻地去模拟,暴力地把p个0加在x后面,然而 p p p的有 1 e 6 1e6 1e6,会超时.我的做法是这样的,首先把 x 1 x1 x1变为string,令其长度为 m 1 m1 m1,
首先直接比较 m 1 + p 1 与 m 2 + p 2 m1+p1与m2+p2 m1+p1m2+p2,谁长谁大.如果相等,把 x 1 与 x 2 补 成 位 数 相 等 的 s t r i n g , 然 后 直 接 比 较 x1与x2补成位数相等的string,然后直接比较 x1x2string,

/*
  给出两个整数 都是x + p个0 
  比较该两个整数大小. 
  2 000
  1900
*/
#include<bits/stdc++.h>
using namespace std;
const int maxn = 3e5+5;
const int INF = 1e9+7;
int main(){
	int T;
	cin>>T;
	while(T--){
		int x1,p1,x2,p2;
		scanf("%d %d %d %d",&x1,&p1,&x2,&p2);
		string s1,s2;
		s1 = to_string(x1);s2 = to_string(x2);
		int m1 = s1.length();int m2 = s2.length();
		if(m1+p1!=m2+p2){
			if(s1.length()+p1>s2.length()+p2) cout<<">\n";
			else cout<<"<\n";
			continue;
		}
		if(m2>m1){
			for(int i=0;i<(m2-m1);i++) s1+="0";
			if(s1==s2) cout<<"=\n";
			else if(s1>s2) cout<<">\n";
			else cout<<"<\n";
		}
		else if(m1==m2){
			if(x1>x2) cout<<">\n";
			if(x1==x2) cout<<"=\n";
			if(x1<x2) cout<<"<\n";
		}
		else if(m1>m2){
			for(int i=0;i<(m1-m2);i++) s2+="0";
			if(s1==s2) cout<<"=\n";
			else if(s1>s2) cout<<">\n";
			else cout<<"<\n";
		}
	}
return 0;}

B. Absent Remainder

题意:给你一个长度为 n n n的数组 a i ai ai,然后要你找 n / 2 n/2 n/2对的 x , y x,y x,y,满足 x % y 不 在 数 组 a 中 x\%y不在数组a中 x%ya.
思路:考虑 x % y x\%y x%y的值域,落在 [ 0 , y − 1 ] [0,y-1] [0,y1],如果我们选取y是最小值,那么任意的 x ! = y x!=y x!=y都一定满足该条件.代码中还排序了一下,选后 n / 2 n/2 n/2个数字.

/*
   2 3 4 5 7 8
   8 7 4 3 5 2
   
*/
#include<bits/stdc++.h>
using namespace std;
const int maxn = 2e5+5;
const int INF = 1e9+7;
int a[maxn];
int main(){
	int T;cin>>T;
	while(T--){
		int n;scanf("%d",&n);
		for(int i=1;i<=n;i++) scanf("%d",&a[i]);
		int m = n / 2;
		sort(a+1,a+1+n);
		for(int i=n-m+1;i<=n;i++) {
			cout<<a[i]<<" "<<a[1]<<"\n";
		}
	}
}

C. Poisoned Dagger

题意:可以简化为以下问题: 给出 n n n个数轴上的点 a i ai ai
寻找一个最小的k,使得总区间长度 [ a i , a i + k ] , [ a i + 1 , a i + 1 + k ] . . . [ a n , a n + k ] [ai,ai+k],[ai+1,ai+1+k]...[an,an+k] [ai,ai+k],[ai+1,ai+1+k]...[an,an+k]长度大于H.
分析:显然,对于k来说,满足单调递增性.如果k是一个可行解,那么k+1也一定是一个可行解.二分k即可.
那么问题转化为计算出总区间长度,记为 s u m sum sum,我们只需要解决两个区间重复的问题.
s u m = ∑ i = 1 n k − o v e r l a p ( a i , a i − 1 ) sum=\sum_{i=1}^n{k-overlap(a_i,a_{i-1})} sum=i=1nkoverlap(ai,ai1). o v e r l a p ( a i , a i − 1 ) 是 指 区 间 前 后 两 个 区 间 的 重 复 部 分 . 不 难 得 o v e r l a p ( a i , a i − 1 ) = a i − 1 + k − a i overlap(a_i,a_{i-1})是指区间前后两个区间的重复部分.不难得overlap(a_i,a_{i-1})=a_{i-1}+k-a_i overlap(ai,ai1).overlap(ai,ai1)=ai1+kai
计算出 s u m sum sum,只需判断sum和H的大小就能写出二分的check.
代码:

#include<bits/stdc++.h>
using namespace std;
const int maxn = 100+5;
const int INF = 1e9+7;
typedef long long ll;
int a[maxn];
int n;ll h;
bool check(ll k){
	ll sum = 0;
	for(int i=1;i<=n;i++){
		if(i==1) sum+=k;
		else {
			ll overlap = a[i-1] + k - a[i];
			if(overlap>0) sum-=overlap;
			sum+=k;
		}
	}
	return sum >= h;
}
int main(){
	int T;cin>>T;
	while(T--){
		scanf("%d %lld",&n,&h);
		for(int i=1;i<=n;i++) scanf("%d",&a[i]);
		ll L = 1,R = h;ll ans;
		while(L<=R){
			ll mid = (L+R)>>1;
			if(check(mid)){
				ans = mid;R = mid-1;
			}
			else L = mid + 1;
		}
		cout<<ans<<"\n"; 
	}
}

D.MEX Sequences

题意:定义一个好序列ak.
对于 1 < = i < = k , 都 满 足 ∣ x i − M e x ( x 1.. x i ) ∣ < = 1 1<=i<=k,都满足|xi-Mex(x1..xi)|<=1 1<=i<=k,xiMex(x1..xi)<=1
现在给你一个长度为n的数组,计算出满足上述条件的子序列个数.
分析:先分析这样合法的序列大概长什么样.通过分析几个样例尤其是最后一个样例与5个0的样例,不难发现合法的序列大概长这两个个样子.
[ 1 , 2 , 2 , 2 , 3 , 3 , 3 ] [1,2,2,2,3,3,3] [1,2,2,2,3,3,3]
[ 1 , 2 , 3 , 4 , 6 , 4 , 6 , 6 , 4 ] [1,2,3,4,6,4,6,6,4] [1,2,3,4,6,4,6,6,4]
计算出子序列的模型我个人认为是这样的:扫描一遍该数组,每扫到一个元素,考虑如何把该元素放到原有的合法子序列仍然合法,然后组成新的子序列.
考虑元素 x x x如何插入到上述两种子序列中.
n u m 1 [ i ] 为 扫 到 当 前 x 元 素 为 止 , [ 0 , 1 , 2... i ] 的 子 序 列 数 目 num1[i]为扫到当前x元素为止,[0,1,2...i]的子序列数目 num1[i]x,[0,1,2...i]
n u m 2 [ i ] 为 扫 到 当 前 x 元 素 为 止 , [ 0 , 1 , 2.. i − 2 , i ] 子 序 列 数 目 . num2[i]为扫到当前x元素为止,[0,1,2..i-2,i]子序列数目. num2[i]x,[0,1,2..i2,i].
对于 n u m 1 [ x ] num1[x] num1[x],x都可以与其子序列组成新的合法序列,故 n u m 1 [ x ] ∗ = 2 num1[x]*=2 num1[x]=2.
同理 n u m 1 [ x − 1 ] 也 是 , x 能 插 入 到 [ 1 , 2 , 3... x − 1 ] 中 , 但 这 部 分 贡 献 应 当 算 到 n u m 1 [ x ] 中 有 : n u m 1 [ x ] + = n u m [ x − 1 ] . num1[x-1]也是,x能插入到[1,2,3...x-1]中,但这部分贡献应当算到num1[x]中有:num1[x]+=num[x-1]. num1[x1],x[1,2,3...x1],num1[x]:num1[x]+=num[x1].
类似地,对于 n u m 2 num2 num2. x 要 么 是 插 到 [ 1 , 2 , 3 , x , x + 2 ] 中 , 要 么 是 插 到 [ 1 , 2 , 3 , 4.. , x − 2 , x ] 中 x要么是插到[1,2,3,x,x+2]中,要么是插到[1,2,3,4..,x-2,x]中 x[1,2,3,x,x+2],[1,2,3,4..,x2,x]
n u m 2 [ x + 2 ] ∗ = 2 , n u m [ x ] ∗ = 2 num2[x+2]*=2,num[x]*=2 num2[x+2]=2,num[x]=2
但 是 要 注 意 n u m 2 的 起 始 条 件 是 [ 1 , 2 , 3... i − 2 ] 这 恰 好 是 n u m 1 [ i − 2 ] 的 数 目 , 所 以 实 际 上 还 有 n u m 2 [ x ] + = n u m 1 [ x − 2 ] 但是要注意num2的起始条件是[1,2,3...i-2]这恰好是num1[i-2]的数目,所以实际上还有num2[x]+=num1[x-2] num2[1,2,3...i2]num1[i2],num2[x]+=num1[x2]
最后答案就是把 n u m 1 , n u m 2 num1,num2 num1,num2全部加起来,代表所有子序列的数目.
然而,上述的转移方程在实际编程却遇到了麻烦,请看如下测试样例 [ 1 , 1 ] [1,1] [1,1],没错在单独一个[1]的开头子序列,我们计算出现了麻烦,它是属于num2情况中的特殊情况,它不能被继续续上2,也不能续上0,只能续上它自己,但在编程中却忽略了它,应当计算上,存放在num2[1]中.同理也有 [ 0 , 0 , 0 , 0 ] 这 样 的 计 数 [0,0,0,0]这样的计数 [0,0,0,0]
所以当 x = 1 时 , 应 当 还 有 n u m 2 [ 1 ] = n u m 2 [ 1 ] + n u m 2 [ 1 ] x=1时,应当还有num2[1]=num2[1]+num2[1] x=1,num2[1]=num2[1]+num2[1],当 x = 0 时 , 应 当 还 有 n u m 1 [ 0 ] + = 1 ; x=0时,应当还有num1[0]+=1; x=0,num1[0]+=1;
注意注意注意:上述的过程是有先后顺序的,在计算过程中, n u m 1 [ x ] ∗ = 2 , n u m 2 [ x ] ∗ = 2 , 是 最 先 执 行 的 num1[x]*=2,num2[x]*=2,是最先执行的 num1[x]=2,num2[x]=2,,如果你放在后边进行,就会进行错误地翻倍,因为 ∗ = 2 *=2 =2实际上代表的是加上上一次自己的子序列数目是多少,如果先进行了其他操作,就会改变了 n u m 1 [ x ] , n u m 2 [ x ] 的 值 , 进 而 错 误 地 ∗ = 2 了 num1[x],num2[x]的值,进而错误地*=2了 num1[x],num2[x],=2
代码:

/*
*/
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int maxn = 5e5+2;
const int INF = 1e9+7;
const int mod = 998244353;
int a[maxn];
int main(){
	int T;
	cin>>T;
	while(T--){
		int n;scanf("%d",&n);
		vector<int> num1(n+5);
		vector<int> num2(n+5);
		for(int i=1;i<=n;i++){
			int x;
			scanf("%d",&x);
			num1[x] = (num1[x] + num1[x]) %mod;
			if(x>0) num1[x] = (num1[x] + num1[x-1])%mod;
			else if(num1[0]==0) num1[0] = 1;
			else num1[0]++;
			num2[x] = (num2[x]+num2[x])%mod;
			if(x>=2) num2[x] = (num2[x]+num1[x-2]) % mod;
			if(x==1) {
				if(num2[1]==0) num2[1] =1;
				else num2[1]++;
			}
			num2[x+2] = (num2[x+2] + num2[x+2] )%mod;
		}
		int ans = 0;
		for(int i=0;i<=n;i++) ans = ((ans+num1[i])%mod+num2[i])%mod;
		cout<<ans<<"\n";
	}
return 0;}


一开始用的数组每次进行初始化让卡时间了,后来改用vector过了

E. Crazy Robot

给一个机器人与一张地图,这个机器人对输入的指令一定不服从,也就是说,只能控制它不去往一个方向.然后给你一个’L’,要求用指令把这个机器人抓回去’L’.然后机器人可能在任意的 ′ . ′ '.' .上,所以要求你输出是否可以由’.‘绝对可以到达’L’点,把这样的’.‘点改为’+’,输出整一张地图.
思路:显然是一个搜索.怎么让机器人前往’L’点呢,假设机器人在’L’点附近的一个点,如果机器人被两面’墙’堵住,我们再控制它不去往第三面,那么机器人一定会前往’L’点.所谓的"墙’就是机器人不能前往的点/前往了就一定会被抓住的点,也就是’+’,‘L’,’#'和地图边界.
代码实现:采用bfs实现,但要注意,在进行墙的计数时,不能把该点的前驱也视为墙,事实上往外扩展的节点是需要去到绝对可以前往’L’的这一集合中,也就是往外走的节点被两个墙堵住,然后再被指令堵一个方向,只能前往前驱被访问过的集合中。
注意,不要用数组存地图,因为n,m规模很大,但是 n ∗ m < = 1 e 6 n*m<=1e6 nm<=1e6,开vector存,
vis数组同理,省空间,防止爆空间.

#include<bits/stdc++.h>
using namespace std;
const int maxn = 3e5+5;
const int INF = 1e9+7;
typedef pair<int,int> pii;
vector<vector<char> > mp;
vector<vector<bool> > vis;
int dx[]={0,0,1,-1};int dy[]={1,-1,0,0};
int n,m;int sx,sy;;
int block(int x,int y,int fx,int fy){
	int B = 0;
	for(int i=0;i<4;i++){
		int nx = x+dx[i];int ny = y+dy[i];
		if(nx==fx&&ny==fy) continue;
		if(nx<1||nx>n||ny<1||ny>m||mp[nx][ny]=='#'||mp[nx][ny]=='L'||mp[nx][ny]=='+') B++;
	}
	return B;
}
bool check(int nx,int ny){
	if(nx<1||nx>n||ny<1||ny>m||mp[nx][ny]=='#'||vis[nx][ny]==1) return false;
	return true;
}
void bfs(){
	queue<pii> q;
	q.push(make_pair(sx,sy));
	vis[sx][sy]=1;
	while(!q.empty()){
		int x = q.front().first;int y=q.front().second;
		q.pop();
		bool L=false;
		if(x==sx&&y==sy) L = true;
		for(int i=0;i<4;i++){
			int nx = x+dx[i];int ny = y+dy[i];
			if(!check(nx,ny)) continue;
			int blocks = block(nx,ny,x,y);
			if(blocks>=2) {
				vis[nx][ny]=1;
				q.push(make_pair(nx,ny));
				mp[nx][ny] = '+';
			}
		}
	}
}
int main(){
	ios_base::sync_with_stdio(0);
    cin.tie(0);
	int T;cin>>T;
	while(T--){
		cin>>n>>m;
		mp.clear();vis.clear();
		mp.resize(n+2);vis.resize(n+2);
		for(int i=1;i<=n;i++){
			mp[i].resize(m+1);vis[i].resize(m+2);
			for(int j=1;j<=m;j++){
				cin>>mp[i][j];
				if(mp[i][j]=='L') sx=i,sy=j;
			}
		}
		bfs();
		for(int i=1;i<=n;i++){
			for(int j=1;j<=m;j++) cout<<mp[i][j];
			cout<<"\n";
		}
	}
return 0;}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

minato_yukina

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值