Codeforces Round #515 (Div. 3)(部分题解)

题目链接

A. Vova and Train

题意:给定一个区间[1,L],一个区间[l,r],给定一个v。问在[1,L]而不在[l,r]内有多少个数是v的除数。

题解:先求总区间v的除数,再减去[l,r]的除数。

#include<bits/stdc++.h>
using namespace std;
int main()
{
	int n;
	cin>>n;
	while(n--)
	{
		long long L,v,l,r,ans=0;
		cin>>L>>v>>l>>r;
		ans=L/v-r/v+(l-1)/v;
		cout<<ans<<'\n';
	}
	return 0;
 } 

B. Heaters

题意:Vova的房子是一个一维坐标系,n个房间,1代表这个房间有加热器,能给[pos−r+1;pos+r−1]的范围房间进行加热。r是给定的,并且对于每个加热器都是一样的。问怎样选择打开最少的加热器,可以使得所有房间都能被加热到。

题解:简单贪心加暴力。首先当我选择了一个房间进行加热时,那么下一个房间选择的范围就是,[i+1,i+r*2-1]。因为对于每一个有加热器的房间,加热范围左右都能覆盖,那么最优的选择是两个房间中间的加热区间没有重叠,所以选择下一个房间的最优解就是对这个区间[i+1,i+r*2-1]从后往前遍历。第一个房间的选择的范围是[1,r]。

#include<bits/stdc++.h>
using namespace std;
const int maxn = 2020;
int a[maxn];
int main()
{
	int n,r;
	cin>>n>>r;
	for(int i=1;i<=n;i++) cin>>a[i];
	
	int cur=0;//之前选择的房间位置
	
	int i=r;//能够贪心选择最远房间的位置 
	
	int flag,ans=0;
	while(1){
		flag=1;
		for(int j=i;j>cur&&flag;--j){
			if(a[j]) {
		    	i=j+2*r-1;//能够贪心选择最远房间的位置 
				ans++;
				flag=0;
				cur=j;//保存现在选择房间的位置	
			}
		}
		if(flag) break;//没有找到满足条件的房间 
		
		if(cur+r-1>=n) break;//已经覆盖了所有房间 
		
		if(i>n) i=n;
	}
	printf("%d\n",flag==1?-1:ans);
}

 

 

C. Books Queries

题意:有一个双向队列。L代表将这个元素放到队列左边,R代表将这个元素放到右边。?代表查询至少删除多少个元素可以使得这个元素在队列最左边或者队列最右边。

题解:一开始想直接用双向队列去模拟,但是数据范围太大会T。后面仔细想了一下,我每次将一个元素添加到队列的时候,他肯定是队列的最左端或者最右段,当我查询这个元素的时候,是不是只要比较后面新添加了多少个L以及多少个R就好了。

比如我要查询元素L1

扫描二维码关注公众号,回复: 6751853 查看本文章

假设将L1添加到队列时的情况是这样的  L1 L L R R R ;三个放队列左边,三个放队列右边。

假设当我要查询L1时队列进行了更新变成了   L L L1 L L R R R R。

如果要使得L1变成最左端元素,那就是查询时左段元素的个数,减去添加L1时左端元素的个数。也就是5-3=2。

如果要使得L1变成最右端元素,那就是查询时右端元素的个数,加上添加L1是左端元素的个数-1。也就是4+(3-1)=6。

那如何快速找到这个元素是在哪个时候进行添加的呢。我们可以定义一个数组pos[maxn]。另pos[L1]=i,就可以保存这个元素的位置了。

#include<bits/stdc++.h>
using namespace std;
const int maxn = 1e6;
int pos[maxn];
struct node{
	int l,r;//保存此时放队列左边的个数,以及放右边的个数 
	int id;//1表示这个元素放左边,2表示这个元素放右边 
}e[maxn];
int main()
{
	int n,l=0,r=0;
	cin>>n;
	for(int i=1;i<=n;i++){
		getchar();
		char c;int x;
		cin>>c>>x;
		
		if(c=='L') l++,e[i].id=1;
		if(c=='R') r++,e[i].id=2;
		
		if(c=='?'){
			int ans=0;
			if(e[pos[x]].id==1){
				ans = min(l-e[pos[x]].l,r+e[pos[x]].l-1);
			}
			if(e[pos[x]].id==2){
				ans = min(r-e[pos[x]].r,l+e[pos[x]].r-1);
			}
			cout<<ans<<endl;
			continue;
		}
		
		e[i].l=l;e[i].r=r;
		
		pos[x]=i;//保存这个元素在结构体的位置 
	}
}

 

 

 

E. Binary Numbers AND Sum

这个e题敲简单,难怪xls十几分钟秒了,后悔比赛的时候没看这个题qwq。

题意:给你两个二进制数也就是01串,a串不动,b串每次右移一位。将每次移动后的a串和b串的 & 之后得到的结果加起来,直到b为0;

题解:前缀和+快速幂。一开始以为是一个大数题,然后又想到位置的奇偶性之类的。。。乱七八糟的想法。最后才发现其实没那么复杂。

a数组是不动的,b在不停往后移。那么对于a的每一位1,是不是都会与在他位置之前的b串的所有1贡献答案呢。

比如 a = 10001   b = 1110011

         10001           10001         10001        10001       10001       10001           10001

      1110011          111001         11100          1110            111             11                   1

对于a数组最高位的1,会与b数组最高位的3个1与贡献答案  3 * 2^4 ;对于a数组最低位的1,会与b数组所有1相与贡献答案5*2^0

最后得到的答案就是53

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int maxn = 2e5+100;
const ll mod =998244353;
int sum[maxn];
char s1[maxn],s2[maxn];

ll powmod(ll a,ll b) {ll res=1;a%=mod; //快速幂 
 for(;b;b>>=1){if(b&1)res=res*a%mod;a=a*a%mod;}return res;}
 
int main()
{
	int n,m;
	cin>>n>>m>>s1+1>>s2+1;
	for(int i=1;i<=m;i++){//求前缀和 
		if(s2[i]=='1') sum[i]=sum[i-1]+1;
		else sum[i]=sum[i-1];
	}
	
	ll ans=0;
	int t=m-n;//m和n不一样,t的作用是将他们的位置对齐 
	
	for(int i=n;i>=1;i--)//因为后面才是低位,所以从后面往前遍历 
		if(s1[i]=='1'&&i+t>=1)//t位负数时,i+t可能为负数 
		    ans=(ans+powmod(2,(n-i))*sum[i+t])%mod;
	cout<<ans<<endl;
}

猜你喜欢

转载自blog.csdn.net/qq_42129242/article/details/90572670