【codeforces】【比赛题解】#948 CF Round #470 (Div.2)

【A】Protect Sheep

题意:

一个\(R*C\)的牧场中有一些羊和一些狼,如果狼在羊旁边就会把羊吃掉。

可以在空地上放狗,狼不能通过有狗的地方,狼的行走是四联通的。

问是否能够保护所有的羊不被狼吃掉,如果能输出方案。

题解:

判断是否有羊的旁边有狼,有的话就-1。没有的话把所有空地换成狗就好。

#include<bits/stdc++.h>
#define F(i,a,b) for(int i=a;i<=(b);++i)
#define F2(i,a,b) for(int i=a;i<(b);++i)
using namespace std;
int n,m,ok=1;
char str[501][505];
int main(){
	scanf("%d%d",&n,&m);
	F(i,1,n){
		scanf("%s",str[i]);
	}
	F(i,1,n){
		F2(j,0,m){
			if(str[i][j]=='S'&&(str[i-1][j]=='W'||str[i][j-1]=='W'||str[i+1][j]=='W'||str[i][j+1]=='W')) ok=0;
		}
	}
	if(ok){
		puts("Yes");
		F(i,1,n){
			F2(j,0,m){
				printf("%c",str[i][j]=='.'?'D':str[i][j]);
			}
			puts("");
		}
	}
	else puts("No");
	return 0;
}

【B】Primal Sport

题意:

初始有一个数\(X_0\),每一次可以对\(X_i\)这个数进行一个操作变成\(X_{i+1}\):

选择一个小于\(X_i\)的质数\(p\),则\(X_{i+1}\)等于\(\left\lceil\frac{X_i}{p}\right\rceil\cdot p\)。

给定了\(X_2\),问最小的可能\(X_0\)。

题解:

令\(p_1\)等于\(X_2\)的最大质因数,那么显然\(X_1\)可能为\([X_2-p_1+1,X_2]\)中的合数。

再对这个范围内的\(X_1\)执行相同的过程即可。

#include<bits/stdc++.h>
#define F(i,a,b) for(int i=a;i<=(b);++i)
using namespace std;
const int INF=0x3f3f3f3f;

int n,ans=INF;

int np[1000001]={1,1},pr[1000001],cnt;
int maxp[1000001];

void init(int num){
	F(i,2,num){
		if(!np[i]) pr[++cnt]=i, maxp[i]=i;
		F(j,1,cnt){
			if(i*pr[j]>num) break;
			np[i*pr[j]]=1;
			maxp[i*pr[j]]=maxp[i];
			if(i%pr[j]==0) break;
		}
	}
}

int main(){
	init(1000000);
	scanf("%d",&n);
	F(i,n-maxp[n]+1,n)
		if(maxp[i]!=i) ans=min(ans,i-maxp[i]+1);
		else ans=min(ans,i);
	printf("%d",ans);
	return 0;
}

【C】Producing Snow

题意:

每天Bob都能用造雪机造出体积\(V_i\)的一堆雪,但是现在是夏天,每天每堆雪都会融化\(T_i\)体积(如果不足\(T_i\)则全部融化掉)。

问每天会融化多少体积的雪?

题解:

简单set+lower_bound,就很显然。注意点:multiset。

#include<bits/stdc++.h>
#define F(i,a,b) for(int i=a;i<=(b);++i)
#define F2(i,a,b) for(int i=a;i<(b);++i)
#define dF(i,a,b) for(int i=a;i>=(b);--i)
#define dF2(i,a,b) for(int i=a;i>(b);--i)
#define eF(i,u) for(int i=h[u];i;i=nxt[i])
#define ll long long
#define ld double
using namespace std;

int n;
ll v[100001],t[100001];
ll ans,sum;
multiset<ll> st;

int main(){
	scanf("%d",&n);
	F(i,1,n) scanf("%lld",v+i);
	F(i,1,n) scanf("%lld",t+i);
	F(i,1,n){
		st.insert(v[i]+sum);
		multiset<ll>::iterator it,it2,it3;
		sum+=t[i];
		ans=t[i]*st.size();
		it=st.lower_bound(sum+1);
		for(it2=st.begin();it2!=st.end()&&it2!=it;){
			ans+=*it2-sum;
			it3=it2; ++it2;
			st.erase(it3);
		}
		printf("%lld\n",ans);
	}
	return 0;
}

【D】Perfect Security

题意:

有一个长度为\(n\)的数组\(A\),和一个等长的数组\(B\),要求你重排数组\(B\),使得序列\(A_1\oplus B_1,A_2\oplus B_2,\cdots,A_n\oplus B_n\)在字典序上最小。

题解:

从1到n遍历每一个\(A_i\),选出当前\(B\)中的让\(A_i\oplus B_i\)最小的\(B_i\)即可,重复\(n\)遍。

那么每次必须要在较快时间内完成,考虑set内按二进制位二分。每次可以\(log^2n\)完成。

#include<bits/stdc++.h>
#define F(i,a,b) for(int i=a;i<=(b);++i)
#define dF(i,a,b) for(int i=a;i>=(b);--i)
using namespace std;

int n,a[300001];
multiset<int> b;

int main(){
	int x;
	scanf("%d",&n);
	F(i,1,n) scanf("%d",a+i);
	F(i,1,n) scanf("%d",&x), b.insert(x);
	F(i,1,n){
		int now=0;
		dF(j,29,0){
			if((a[i]>>j)&1){
				now+=1<<j;
				multiset<int>::iterator it1=b.lower_bound(now), it2=b.lower_bound(now+(1<<j));
				if(it1==it2)
					now-=1<<j;
			}
			else{
				multiset<int>::iterator it1=b.lower_bound(now), it2=b.lower_bound(now+(1<<j));
				if(it1==it2)
					now+=1<<j;
			}
		}
		b.erase(b.lower_bound(now));
		printf("%d ",a[i]^now);
	}
	return 0;
}

【E】Picking Strings

题意:

对于一个只含有\(\textbf{A,B,C}\)的字符串,可以做以下变化:

\(\textbf{A}\Rightarrow \textbf{BC}\)

\(\textbf{B}\Rightarrow \textbf{AC}\)

\(\textbf{C}\Rightarrow \textbf{AB}\)

\(\textbf{AAA}\Rightarrow \varnothing\)

给定两个字符串\(S\)和\(T\),有\(Q\)个询问:

每个询问为问\(S[a...b]\)能否通过以上变换变成\(T[c...d]\)。

题解:

考虑\(\textbf{B}\Rightarrow A\textbf{C}\Rightarrow AA\textbf{B}\Rightarrow \textbf{AAA}C\Rightarrow C\)和\(\textbf{C}\Rightarrow A\textbf{B}\Rightarrow AA\textbf{C}\Rightarrow \textbf{AAA}B\Rightarrow B\)。

这表示了\(\textbf{B}\;\textbf{C}\)之间可以互相转化。

考虑\(\textbf{B}\Rightarrow A\textbf{C}\Rightarrow AA\textbf{B}\Rightarrow AAA\textbf{C}\Rightarrow A\textbf{AAA}B\Rightarrow AB\)和\(A\textbf{B}\Rightarrow AA\textbf{C}\Rightarrow \textbf{AAA}B\Rightarrow B\)。

这表示了一个\(\textbf{B}\)之前的\(\textbf{A}\)的个数可以任意地增加或减少,同理\(\textbf{C}\)也一样。

那么又因为\(\textbf{A}\)可以变成\(\textbf{BC}\),也就是说原字符串只要有至少一个\(\textbf{B}\)或者\(\textbf{C}\),就可以把\(\textbf{B}\)或者\(\textbf{C}\)的个数任意地加二。

所以原字符串和目标字符串中的\(\textbf{B}\)或者\(\textbf{C}\)的个数必须奇偶性相同,并且目标字符串的\(\textbf{B}\)或者\(\textbf{C}\)的个数必须大于等于原字符串的个数。

最后考虑字符串末尾的\(\textbf{A}\),它们之后没有任何一个\(\textbf{B}\)或者\(\textbf{C}\)。

那么可以实现个数减三或者把其中一个\(\textbf{A}\)变成\(\textbf{BC}\),这两种操作都无法增加字符串末尾的\(\textbf{A}\)的个数。

那么只要原字符串中末尾\(\textbf{A}\)的个数小于目标字符串中的个数,就无法实现。

如果原字符串中\(\textbf{B}\)或者\(\textbf{C}\)的个数等于目标字符串中的个数,就不能进行第二个操作,只能进行第一个操作。

那么分类讨论就到此结束。

综上所述,我们要知道原字符串和目标字符串的\(\textbf{B}\)或者\(\textbf{C}\)的个数和末尾的\(\textbf{A}\)的个数。

可以用简单的前缀和实现。

代码奉上:

#include<bits/stdc++.h>
#define F(i,a,b) for(int i=a;i<=(b);++i)
using namespace std;

char str1[100005],str2[100005];
int len1,len2;
int sbc1[100005],sbc2[100005];
int sua1[100005],sua2[100005];
int Q;

int main(){
	scanf("%s%s",str1+1,str2+1);
	len1=strlen(str1+1), len2=strlen(str2+1);
	F(i,1,len1) sbc1[i]=sbc1[i-1]+(str1[i]=='B'||str1[i]=='C');
	F(i,1,len2) sbc2[i]=sbc2[i-1]+(str2[i]=='B'||str2[i]=='C');
	F(i,1,len1) sua1[i]=str1[i]=='A'?sua1[i-1]+1:0;
	F(i,1,len2) sua2[i]=str2[i]=='A'?sua2[i-1]+1:0;
	scanf("%d",&Q);
	while(Q--){
		int a,b,c,d;
		scanf("%d%d%d%d",&a,&b,&c,&d);
		int sumbc1=sbc1[b]-sbc1[a-1], sumbc2=sbc2[d]-sbc2[c-1];
		int sufa1=min(sua1[b],b-a+1), sufa2=min(sua2[d],d-c+1);
		if((sumbc1&1)!=(sumbc2&1)||sumbc1>sumbc2||sufa1<sufa2) {putchar('0'); continue;}
		if(sumbc1==sumbc2)
			if(sufa1%3==sufa2%3) putchar('1');
			else putchar('0');
		else
			if(sumbc1==0) putchar(sufa1>sufa2?'1':'0');
			else putchar('1');
	}
	return 0;
}

转载于:https://www.cnblogs.com/PinkRabbit/p/8548843.html

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值