Technocup 2020 - Elimination Round 1补题

慢慢来。

题目册

题目 A B C D
tag math strings greedy dp
状态
//∅,√,×

想法

A. CME

res tp A
题意:有\(n\)根火柴,额外填上\(k(k≥0)\)根火柴棍,使得\(n+k\)能分成三份\(a,b,c\),每份至少有一根火柴,满足\(a+b = c\),问\(k\)最小是多少
满足方程
\(a+b+c = n+k\)
\(a + b = c\)
\(2*c = n+k\)
\(n\)是偶数,那么\(k\)为零,反之\(k\)\(1\)
特别地,\(n\)至少要为\(4\),才能凑出一个等式\(1+1=2\)

#include<bits/stdc++.h>
#define rep(i,a,b) for(int i=(a);i<=(b);++i)
#define per(i,a,b) for(int i = (a);i>=(b);--i)
#define fo(i,a,b) for(int i =(a);i<(b);++i)
#define de(x) cout<<#x<<" = "<<x<<endl;
#define endl '\n'
#define mem(a,b) memset(a,b,sizeof(a));
#define ls(p) ((p)<<1)
#define rs(p) (((p)<<1)|1)
using namespace std;
typedef long long ll;
const int mn = 105;

int n,q;
int main(){
	cin>>q;
	while(q--){
		cin>>n;
		if(n < 4)
			cout<<4 - n<<endl;
		else
			cout<< (n&1) <<endl;
	}
}

B. Strings Equalization

res tp B
题意:给出两个仅包含小写字母的字符串,问是否存在某个字符在两串中都出现过

#include<bits/stdc++.h>
#define rep(i,a,b) for(int i=(a);i<=(b);++i)
#define per(i,a,b) for(int i = (a);i>=(b);--i)
#define fo(i,a,b) for(int i =(a);i<(b);++i)
#define de(x) cout<<#x<<" = "<<x<<endl;
#define endl '\n'
#define mem(a,b) memset(a,b,sizeof(a));
#define ls(p) ((p)<<1)
#define rs(p) (((p)<<1)|1)
using namespace std;
typedef long long ll;
const int mn = 105;


char s[mn],t[mn];
int q,m;
bool vis[27],ans;
int main(){
	cin>>q;
	while(q--){
		cin>>s>>t;
		ans = 0;
		mem(vis,0);
		m =strlen(s);
		rep(i,0,m-1)	vis[ s[i]-'a' ] = 1;
		rep(i,0,m-1) if(vis[t[i]-'a']){
			ans = 1;break;
		}
		if(ans) cout<<"YES"<<endl;
		else cout<<"NO"<<endl;
	}
}



C. Save the Nature

res tp C
题意:给定一个整数序列\(p_i,i\in[1,n]\),你可以对其进行打乱顺序重新排列。定义了一个规则,下标是\(a\)的倍数的,可以有\(x%\)的贡献,下标是\(b\)的倍数的,可以有\(y%\)的贡献,问,在重排之后,按上述规则,从\(1\)开始,最少需要多少个连续的数,使得总贡献不小于\(k\)
贪心地考虑,下标若既是\(a\)的倍数,又是\(b\)的倍数,那岂不是能贡献两份吗?我们先按从大到小的顺序钦定这类下标的元素值,之后再依照\(x\)\(y\)的大小关系继续钦定剩下的对答案有贡献的位置,遍历区间\([1,i]\),执行上述操作,直到遇到第一个总贡献不小于\(k\)的下标,而那就是我们想要得到的最终答案。

#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cstring>
#include<queue>
#define rep(i,a,b) for(int i=(a);i<=(b);++i)
#define per(i,a,b) for(int i = (a);i>=(b);--i)
#define fo(i,a,b) for(int i =(a);i<(b);++i)
#define de(x) cout<<#x<<" = "<<x<<endl;
#define endl '\n'
#define ls(p) ((p)<<1)
#define rs(p) (((p)<<1)|1)
using namespace std;
typedef long long ll;
const int mn = 2e5+10;
int n, q, na, nb, nc, x, y, a, b;
ll p[mn], k;
bool cmp1(ll a,ll b){return a>b;}
int ans;
inline ll gcd(ll x,ll y){
	return (y == 0? x:gcd(y,x%y));
}
inline ll solve(int num){
	na = num/a;nb = num/b;
	nc = na&&nb?num/(a/gcd(a,b)*b):0; nb-=nc;na-=nc;
	ll res = 0;
	res += (p[nc] - p[0])*(x+y);
	if(x > y){
		res += (p[nc + na]- p[nc])*x;
		res += (p[nc + na + nb] - p[nc + na])*y;
	}
	else{
		res += (p[nc + nb] - p[nc])*y;
		res += (p[nc + na + nb] - p[nc + nb])*x;
	}
	return res/100;
}

int main(){
	scanf("%d",&q);
	while(q--){
		scanf("%d",&n);
		rep(i,1,n) cin>>p[i];
		sort(p+1,p+1+n,cmp1);
		rep(i,1,n) p[i] += p[i-1];
		scanf("%d%d%d%d",&x,&a,&y,&b);
		scanf("%lld",&k);
		ans = -1;
		rep(i,1,n)	if( solve(i) >=k ){
			ans = i;
			break;
		}
		printf("%d\n",ans);
	}
}

D. Sequence Sorting

res tp D
题意:给定一个序列,可以进行进行一种操作:选定一个x,之后将值为x的所有数字移动到序列的最左端或最右端,问最少进行多少次操作,使得序列满足单调不减

首先取出原序列中我们需要的信息,而其他的信息可以忽略。求出其每种元素的两个特征:初次出现位置和末次出现位置。
按元素值从小到大的序列就是我们最终要得到的序列。为了让操作数尽可能小,我们需要省去一些力气。如果原序列和终序列在某些地方是“相似”的,岂不是可以省去对这部分“相似”的操作吗?
具体地说,对终序列的两个数值紧邻的元素,若他们的分布区间不相交,且保证较小的元素的区间在较大的元素的左侧,那么这两种元素的相对位置在两个序列中是相等的,也就是说,它们对操作数没有贡献。
反之,至少要对其中的一个元素进行操作,同时,这也意味着,被操作元素那一侧的所有元素都要被操作。
综上,不需要操作的元素一定在终序列中是连续,于是可以dp出终序列满足条件的最长子区间,再用元素种类数减之即为答案

#include<bits/stdc++.h>
#define rep(i,a,b) for(int i=(a);i<=(b);++i)
#define per(i,a,b) for(int i = (a);i>=(b);--i)
#define fo(i,a,b) for(int i =(a);i<(b);++i)
#define de(x) cout<<#x<<" = "<<x<<endl;
#define endl '\n'
#define mem(a,b) memset(a,b,sizeof(a));
#define ls(p) ((p)<<1)
#define rs(p) (((p)<<1)|1)
using namespace std;
typedef long long ll;
const int mn = 3e5+10;
int q,n;
struct E{
    int v,l,r;
}e[mn];
int vis[mn],t,cnt,mdp,dp;
bool cmp(E a,E b){return a.v <b.v;}
int main(){
	scanf("%d",&q);
	while(q--){
		cnt = 1;
		scanf("%d",&n);
		rep(i,1,n){
			scanf("%d",&t);
			if(vis[t])
				e[vis[t]].r	= i;
			else{
				e[cnt].l = e[cnt].r = i;
				e[cnt].v = t;
				vis[t] = cnt++;
			}
		}
		sort(e+1,e+cnt,cmp);
		mdp = dp = 1;
		int cnt1 = cnt - 1;
		rep(i,2,cnt1){
			if(e[i-1].r <e[i].l) ++dp;
			else dp = 1;
			mdp = max(mdp,dp);
		}
		printf("%d\n",cnt - 1 - mdp);
		rep(i,1,cnt1) vis[e[i].v] = 0;
	}
}

posted @ 2019-10-09 22:32  不学无术/眼高手低  阅读(363)  评论(0)    收藏  举报