Codeforces Round #486 (Div. 3)(A-F)题解

传送门

A. Diverse Team

思路:用一个 v i s [ ] vis[] vis[]数组维护出现过的数即可。

时间复杂度: O ( n ) O(n) O(n)

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N=1e5+5,M=1e6+5,inf=0x3f3f3f3f,mod=1e9+7;
#define mst(a) memset(a,0,sizeof a)
#define lx x<<1
#define rx x<<1|1
#define reg register
#define PII pair<int,int>
#define fi first 
#define se second
int a[N],vis[N];
int main(){
	int n,k;
	cin>>n>>k;
	int cnt=0;
	for(int i=1;i<=n;i++){
		cin>>a[i];
		if(!vis[a[i]]&&cnt<k) vis[a[i]]=i,cnt++;
	}
	if(cnt!=k) puts("NO"),exit(0);
	puts("YES");
	for(int i=1;i<=100;i++)
		if(vis[i]) printf("%d ",vis[i]); 
	return 0;
}

B. Substrings Sort

思路:因为题目要求每个串是前一个串的子串,显然后一个串长度要大于等于前一个串,所以我们考虑对字符串按长度排序,如果长度相同就按字典序排即可,最后顺序扫一遍是否都满足情况。

时间复杂度: O ( n l o g n ) O(nlogn) O(nlogn)

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N=105+5,M=1e6+5,inf=0x3f3f3f3f,mod=1e9+7;
#define mst(a) memset(a,0,sizeof a)
#define lx x<<1
#define rx x<<1|1
#define reg register
#define PII pair<int,int>
#define fi first 
#define se second
string a[N];
bool cmp(string a,string b){
	return a.size()==b.size()?a<b:a.size()<b.size(); 
}
int main(){
	int n;
	cin>>n;
	for(int i=0;i<n;i++)
		cin>>a[i];
	sort(a,a+n,cmp);
	int ok=1;
	for(int i=1;i<n;i++){
		if(a[i].find(a[i-1])==a[i].npos) return puts("NO"),0; 
	}
	puts("YES");
	for(int i=0;i<n;i++) cout<<a[i]<<endl;
	return 0;
}

C. Equal Sums

思路:我的方法是:用一个 m a p map map储存所有之前出现过的和对于的情况。然后遇到相同的和特判一下即可。

时间复杂度: O ( n + s u m n i ) O(n+sum_{n_i}) O(n+sumni)

官方题解是开一个 v e c t o r vector vector存入所有和的情况,然后排序对相邻的数进行特判。

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N=3e5+5,M=1e6+5,inf=0x3f3f3f3f,mod=1e9+7;
#define mst(a) memset(a,0,sizeof a)
#define lx x<<1
#define rx x<<1|1
#define reg register
#define PII pair<int,int>
#define fi first 
#define se second
int b[N];
map<int,PII>mp;
int main(){
	int n;
	scanf("%d",&n);
	int cnt=0;
	int x=-1,y=-1,id1=-1,id2=-1;
	int f=0;
	for(int i=1;i<=n;i++){
		int m,sum=0;
		scanf("%d",&m);
		for(int j=1;j<=m;j++) scanf("%d",&b[j]),sum+=b[j];
		if(!f)
		for(int j=1;j<=m;j++) {
			//printf("%d %d %d\n",sum-b[j],mp[sum-b[j]].fi,mp[sum-b[j]].se);
			if(mp[sum-b[j]].fi==0&&mp[sum-b[j]].se==0)
			mp[sum-b[j]]={i,j};
			else if(i!=mp[sum-b[j]].fi){
				x=mp[sum-b[j]].fi,id1=mp[sum-b[j]].se;
				y=i,id2=j;
				f=1;
			}
		}
	}
	if(!f) puts("NO");
	else {
		puts("YES");
	printf("%d %d\n%d %d\n",x,id1,y,id2); 
	}
 	return 0;
}

D. Points and Powers of Two

这题我 W A WA WA了好多次,才考虑完全,需要注意细节。

思路:位运算暴力的好题。

观察可得满足条件的序列长度最多为3,因为一个数不能同时和 3 3 3个数之差为 2 2 2的次方,且当长度为 3 3 3时必须满足相邻两数之差相等都为 2 2 2的次方,不然第1个数和第3个数不能满足条件。所以我们暴力枚举,对于每个数考虑它的左边和右边是否存在这样的数,如果有长度为 3 3 3的序列直接输出结束,否则看是否有长度为 2 2 2的序列,如果都没有就输出第一个数即可(依题意一个数也算满足)。

时间复杂度: O ( 32 n ) O(32n) O(32n)

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N=2e5+5,M=1e6+5,inf=0x3f3f3f3f,mod=1e9+7;
#define mst(a) memset(a,0,sizeof a)
#define lx x<<1
#define rx x<<1|1
#define reg register
#define PII pair<int,int>
#define fi first 
#define se second
ll a[N];
int main(){
	int n;
	scanf("%d",&n);
	for(int i=1;i<=n;i++){
		scanf("%lld",&a[i]);
	}
	sort(a+1,a+n+1);
	ll ans1,ans2;
	int f=0;
	for(int i=1;i<=n;i++){
		ll l=1e15,r=1e15;
		for(int j=0;j<32;j++){
			ll x=(1LL<<j);
		 	int p=lower_bound(a+1,a+n+1,a[i]+x)-a;
		 	if(p!=n+1&&a[p]==a[i]+x) r=a[i]+x;
		 	p=lower_bound(a+1,a+i,a[i]-x)-a;
		 	if(p!=n+1&&a[p]==a[i]-x) l=a[i]-x;
		}
		//printf("l=%lld,r=%lld,i=%d\n",l,r,i);
		if(l!=1e5&&r!=1e15&&(a[i]-l==r-a[i])) {
			printf("3\n%lld %lld %lld\n",l,a[i],r);
			return 0;
		}
		if(l!=1e15) ans1=l,ans2=a[i],f=1;
		if(r!=1e15) ans1=a[i],ans2=r,f=1;
	}
	if(f) printf("2\n%lld %lld\n",ans1,ans2);
	else printf("1\n%lld\n",a[1]);
	return 0;
}

E. Divisibility by 25

思路:此题贪心的很巧妙,没想到。

首先观察可得,显然答案的最后两位肯定是 { 00 , 25 , 50 , 75 } \{00,25,50,75\} {00,25,50,75}这4种情况。

所以我们考虑答案为这 4 4 4种情况的最小操作数。

根据贪心思想,我们从字符串的后面往前查找,答案的最后一位和答案的倒数第二位,他们的移动次数就是位置之差,即分别对应: ( l e n − 1 ) − p o s 最 后 一 位 (len-1)-pos_{最后一位} (len1)pos ( l e n − 2 ) − p o s 倒 数 第 二 位 (len-2)-pos_{倒数第二位} (len2)pos

因为题目要求不能有前导 0 0 0,所以我们还需要将第一个非0数移动到首位即可。

这个操作的次数为 c n t 0 cnt_0 cnt0

所以总操作数为: c n t 0 + ( l e n − 1 ) − p o s 最 后 一 位 + ( l e n − 2 ) − p o s 倒 数 第 二 位 cnt_0+(len-1)-pos_{最后一位}+(len-2)-pos_{倒数第二位} cnt0+(len1)pos+(len2)pos

此外具体实现的时候,我们需要将 p o s 最 后 一 位 pos_{最后一位} pos找到后删掉,这样对于第一种情况就不会找到同一个 0 0 0

假设目标是 “ x y ” “xy” xy,若 x x x原来在 y y y左边,则删掉 y y y后,位于 y y y右左的格子不会移动, p o s 1 pos_1 pos1不会变,移动的次数是 ( l e n − 2 ) − p o s 倒 数 第 二 位 (len-2)-pos_{倒数第二位} (len2)pos

x x x原来在 y y y右边,删掉 y y y后, p o s 1 pos_1 pos1会加1,由于是两两交换的移动,位于 y y y右边的格子会向左平移一格,即 x x x会向左平移一格,就抵消了,还是 ( l e n − 2 ) − p o s 倒 数 第 二 位 (len-2)-pos_{倒数第二位} (len2)pos

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N=1e5+5,M=1e6+5,inf=0x3f3f3f3f,mod=1e9+7;
#define mst(a) memset(a,0,sizeof a)
#define lx x<<1
#define rx x<<1|1
#define reg register
#define PII pair<int,int>
#define fi first 
#define se second
string a;
int len;
char ch[4][3]={"00","25","50","75"};
int fun(char x,char y){
	 string b=a;
	 int p2=b.rfind(y);
	 if(p2==b.npos) return inf;
	 b.erase(p2,1);
	 int p1=b.rfind(x);
	 if(p1==b.npos) return inf;
	 int cnt=0;
	 while(b[cnt]=='0') cnt++;
	 return cnt+(len-2)-p1+(len-1)-p2; 
}
int main(){
	cin>>a;
	len=a.size();
	int ans=inf;
	for(int i=0;i<4;i++)
		ans=min(ans,fun(ch[i][0],ch[i][1]));
	if(ans==inf) puts("-1");
	else printf("%d\n",ans);
	return 0;
}

F. Rain and Umbrellas

思路: d p dp dp
考虑令到达位置 i i i不被淋湿的最小花费为 d p [ i ] dp[i] dp[i]

显然如果位置 i − 1 i-1 i1不下雨,则有 d p [ i ] = d p [ i − 1 ] dp[i]=dp[i-1] dp[i]=dp[i1],这里直接赋值的原因是如果位置 i i i下雨的话, d p [ i + 1 ] dp[i+1] dp[i+1]会遍历能带伞到达的位置取最小值所以是没有关系的。

i − 1 i-1 i1下雨的话,显然我们要从 j ∈ [ 0 , i ) j\in[0,i) j[0,i)找到带伞且花费最小的位置,加上带伞过来的花费,然后取最小值更新答案即可。即: d p [ i ] = m i n ( d p [ i ] , d p [ j ] + ( i − j ) × c o s t [ j ] ) dp[i]=min(dp[i],dp[j]+(i-j)\times cost[j]) dp[i]=min(dp[i],dp[j]+(ij)×cost[j])

时间复杂度: O ( a 2 ) O(a^2) O(a2)

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N=2e3+5,M=1e6+5,inf=0x3f3f3f3f,mod=1e9+7;
#define mst(a) memset(a,0,sizeof a)
#define lx x<<1
#define rx x<<1|1
#define reg register
#define PII pair<int,int>
#define fi first 
#define se second
int rain[N],umb[N],dp[N];
int main(){
	int a,n,m;
	scanf("%d%d%d",&a,&n,&m);
	memset(dp,0x3f,sizeof dp);
	for(int i=1;i<=n;i++){
		int l,r;
		scanf("%d%d",&l,&r);
		for(int i=l;i<r;i++) rain[i]=1;
	}
	for(int i=1;i<=m;i++){
		int x,p;
		scanf("%d%d",&x,&p);
		if(!umb[x]) umb[x]=p;
		else umb[x]=min(umb[x],p);
	} 
	dp[0]=0;
	for(int i=1;i<=a;i++){
		if(!rain[i-1]) dp[i]=dp[i-1];
		else {
			for(int j=0;j<i;j++)
				if(umb[j])
					dp[i]=min(dp[i],dp[j]+(i-j)*umb[j]);
		}
	}
	printf(dp[a]==inf?"-1":"%d\n",dp[a]);
	return 0;
}