CF666Div2题解与总结

先放个链接:https://codeforc.es/contest/1397

中规中矩的一场比赛吧,但没有AK...考场最后一题没过,赛后想想主要原因还是D题意看错,(英文不好),导致打得很慢(同样A4题,有人rk30,我rk400)

写个简单题解吧,重点讲一下C和E

A题:送分题,没什么好说的,判一下字母数是否平均分成n份就做完了

/*A. Juggling Letters*/ 
#include<cstdio>
#include<iostream>
#include<cstring>
#include<algorithm>
using namespace std;
#define ll long long
char str[1010];
int cnt[27];
int main()
{
	int t;
	cin >> t;
	while(t--){
		memset(cnt,0,sizeof(cnt));
		int n;
		scanf("%d",&n);
		for(int i = 1; i <= n; ++i){
			scanf("%s",str+1);
			int len = strlen(str+1);
			for(int j = 1; j <= len; ++j)
				cnt[str[j] - 'a']++;
		}
		bool flag = true;
		for(int i = 0; i <= 'z' - 'a'; ++i){
			if(cnt[i] % n)		flag = false;
		}
		if(flag)	puts("YES");
		else	puts("NO");
	}
	return 0;
}

  B题依然送分,题面看着很可怕,但其实我们可以意识到c^n次方非常大(即有很多情况是可以提前判掉的),所以可以暴枚这个数组,然后有个显然的贪心就是给定两个数组a,b,要最小化∑|a-bi|,就要把a,b排序后按位减

/*B. Power Sequence*/
#include<cstdio>
#include<iostream>
#include<cstring>
#include<algorithm>
#include<cmath>
using namespace std;
#define ll long long
const int maxn = 1e5 + 10;
ll a[maxn],p[maxn];
int read(){
	char c = getchar();
	int x = 0;
	while(c < '0' || c > '9')		c = getchar();
	while(c >= '0' && c <= '9')		x = x * 10 + c - 48,c = getchar();
	return x;
}
const ll inf = 1e15;
int main()
{
	int n = read();
	ll ans = 2e18;
	for(int i = 0; i < n; ++i)
		a[i] = read();
	sort(a,a+n) ;
	ll x = 1;
	while(1){
		bool flag = false;
		p[0] = 1;
		for(int i = 1; i < n; ++i){
			p[i] = p[i-1] * x;
			if(p[i] >= inf){
				flag = true;
				break;
			}
		}
		if(flag)	break;
		x++;
		ll res = 0;
		for(int i = 0; i < n; ++i)
			res += abs(p[i] - a[i]);
		ans = min(ans,res);
	}
	printf("%lld\n",ans);
	return 0;
}

  C题比较有意思,为什么构造是3次?我们可以先把an搞成0(这个是显然可以的,因为任何数都是1的倍数),然后分别对1~n-1,1~n搞,问题就转化为求a * (n-1)  - b * n = -a[i] 

因为对于任意n,都有gcd(n,n-1) = 1,所以根据Bezout定理,这个不定方程一定有整数解,拓欧求一下即可

/*C. Multiples of Length*/
#include<cstdio>
#include<iostream>
#include<cstring>
#include<algorithm>
using namespace std;
#define ll long long
const int maxn = 1e5 + 10;
int a[maxn];
ll b[maxn];
int read(){
	int f = 1;
	int x = 0;
	char c = getchar();
	while(c < '0' || c > '9')		f = (c == '-')?-1:f,c = getchar();
	while(c >= '0' && c <= '9')		x = x * 10 + c - 48,c = getchar();
	return x * f;
}
ll exgcd(ll a,ll b,ll &x,ll &y){
	if(b == 0){
		x = 1,y = 0;
		return a;
	}
	ll d = exgcd(b,a%b,x,y);
	ll z = x;x = y;y = z - (a / b) * y;
	return d;
}
int main()
{
	int n = read();
	for(int i = 1; i <= n; ++i)
		a[i] = read();
	if(n == 1){
		printf("1 1\n");
		printf("%d\n",-1*a[1]);
		printf("1 1\n0\n");
		printf("1 1\n0\n");
		return 0;
	}
	if(n == 2){
		printf("1 1\n%d\n",-1*a[1]);
		printf("2 2\n%d\n",-1*a[2]);
		printf("1 1\n0\n");
		return 0;
	}
	if(n == 3){
		printf("1 1\n%d\n",-1*a[1]);
		printf("2 2\n%d\n",-1*a[2]);
		printf("3 3\n%d\n",-1*a[3]);
		return 0;
	}
	ll x = 0,y = 0;
	exgcd(n,n-1,x,y);
	printf("1 1\n%d\n",-1*a[1]);
	printf("%d %d\n",2,n);
	for(int i = 2; i <= n; ++i){
		printf("%lld ",1ll * x * (n - 1) * a[i]);
	}
	puts("");
	printf("%d %d\n",1,n);
	for(int i = 1; i <= n; ++i){
		if(i == 1)		printf("%d ",0);
		else	printf("%lld ",1ll * y * n * a[i]);
	}
	return 0;
}

  D题不说了,简单贪心,每次取能取的最大,就过了

/*Stoned Games*/
#include<cstdio>
#include<iostream>
#include<cstring>
#include<algorithm>
#include<queue>
using namespace std;
#define ll long long
int a[110];
struct Num{
	int id,v;
};
bool operator < (Num A,Num B) {
	return A.v < B.v;
}
priority_queue<Num>q;
int read(){
	char c = getchar();
	int x = 0;
	while(c < '0' || c > '9')		c = getchar();
	while(c >= '0' && c <= '9')		x = x * 10 + c - 48,c = getchar();
	return x;
}
int main()
{
	int t = read();
	while(t--){
		int n = read();
		while(!q.empty())	q.pop();
		for(int i = 1; i <= n; ++i)
			a[i] = read(),q.push(Num{i,a[i]});
		int cnt = 0;
		int lst = 0;
		while(1){
			if(q.empty())	break;
			Num now = q.top();Num A;
			if(now.id == lst){
				//if(q.empty())	break;
				q.pop();
				if(q.empty())	break;
				A = q.top();
				q.pop();
				lst = A.id;
				if(A.v >= 2)
					q.push(Num{A.id,A.v-1});
				q.push(now);
			}
			else{
				lst = now.id;
				q.pop();
				if(now.v >= 2)
					q.push(Num{now.id,now.v-1});
			}
			cnt++;
		}
		if(cnt & 1)		puts("T");
		else	puts("HL");
	}
	return 0;
}

  E题比较有意思,对于这种有一堆操作的,一般都只有几种操作有用,其实可以发现,对于一组怪,要么直接全部杀死,要么就只有boss剩1滴血,又因为每组怪都要处理,所以最优解钟最多只有前一个的怪还没有打完(即boss剩一滴血的情况),所以可以根据这个性质DP,具体看代码,有注释

/*E Monster Invader*/
#include<cstdio>
#include<iostream>
#include<cstring>
#include<algorithm>
using namespace std;
#define ll long long
const int maxn = 1e6 + 10;
ll f[maxn][2];/*f[i][0] (1~i has finished) f[i][1] (only the boss of i hasn't finished)*/
ll A[maxn],B[maxn],C[maxn];
ll read(){
	char c = getchar();
	ll x = 0;
	while(c < '0' || c > '9')	 	c = getchar();
	while(c >= '0' && c <= '9')		x = x * 10 + c - 48,c = getchar();
	return x;
}
int a[maxn];
int main()
{
	int n = read();
	ll r1 = read(),r2 = read(),r3 = read(),d = read();
	for(int i = 1; i <= n; ++i)		a[i] = read();
	memset(f,0x3f,sizeof(f));
	f[0][0] = 0;
	for(int i = 1; i <= n; ++i){
		A[i] = min(min(r2,r1),r3);/*一击必杀最小代价*/
		B[i] = min(r3,r1) * a[i] + r3;/*直接清理掉的最小代价*/
		C[i] = min((min(r3,r1) * a[i] + r1),r2);/*剩boss一滴血的最小代价*/
	}
	for(int i = 1; i <= n; ++i) {
		if(i == 1)
			f[i][0] = B[i],f[i][1] = f[i-1][0] + C[i];
		else{
			int k = (i == n)?2:3;
			f[i][0] = min(min(f[i-1][0] + B[i] + d,f[i-1][0] + C[i] + 3 * d + A[i]),min(f[i-1][1] + k * d + B[i] + A[i-1],
			f[i-1][1] + 3 * d + C[i] + A[i-1] + A[i]));
			f[i][1] = f[i-1][0] + C[i] + d;
		}
		//printf("f[%d][0] = %lld f[%d][1] = %lld\n",i,f[i][0],i,f[i][1]);
	}
	printf("%lld\n",f[n][0]);
	return 0;
}

  

 

posted @ 2020-09-01 21:02  y_dove  阅读(318)  评论(0编辑  收藏  举报