Codeforces AIM Tech Round (Div. 2)解题报告

A. Save Luke
time limit per test
1 second
memory limit per test
256 megabytes
input
standard input
output
standard output

Luke Skywalker got locked up in a rubbish shredder between two presses. R2D2 is already working on his rescue, but Luke needs to stay alive as long as possible. For simplicity we will assume that everything happens on a straight line, the presses are initially at coordinates 0 and L, and they move towards each other with speed v1 and v2, respectively. Luke has width d and is able to choose any position between the presses. Luke dies as soon as the distance between the presses is less than his width. Your task is to determine for how long Luke can stay alive.

Input

The first line of the input contains four integers dLv1v2 (1 ≤ d, L, v1, v2 ≤ 10 000, d < L) — Luke's width, the initial position of the second press and the speed of the first and second presses, respectively.

Output

Print a single real value — the maximum period of time Luke can stay alive for. Your answer will be considered correct if its absolute or relative error does not exceed 10 - 6.

Namely: let's assume that your answer is a, and the answer of the jury is b. The checker program will consider your answer correct, if .

Sample test(s)
input
2 6 2 2
output
1.00000000000000000000
input
1 9 1 2
output
2.66666666666666650000
Note

In the first sample Luke should stay exactly in the middle of the segment, that is at coordinates [2;4], as the presses move with the same speed.

In the second sample he needs to occupy the position . In this case both presses move to his edges at the same time.

题意:

在区间[0,L]内选取一个长为d的区间,每秒原区间左端向右移v1,右端向左移v2求使得该区间最长的选址能维持的时间


题解:

二分实数判断即可


#include<iostream>
#include<cstdio>
#include<cstdlib>
#include<algorithm>
#include<cstring>
#include<cmath>
#include<vector>
#include<queue>
using namespace std;

typedef double DB;

DB d,L,v1,v2,ans;

bool Judge(DB now)
{
	//if (L - v2*now - v1*now <= 0) return false;
	return d <= (L - v2*now - v1*now);
}

int main()
{
	#ifdef YZY
		   freopen("yzy.txt","r",stdin);
	#endif
	
	DB l = 0; 
	DB r = 1E5;
	cin >> d >> L >> v1 >> v2;
	for (int i = 1; i <= 60; i++) {
		DB mid = (l+r) / 2;
		if (Judge(mid)) l = mid;
		else r = mid;
	}
	printf("%.6f",l);
	return 0;
}


B. Making a String
time limit per test
1 second
memory limit per test
256 megabytes
input
standard input
output
standard output

You are given an alphabet consisting of n letters, your task is to make a string of the maximum possible length so that the following conditions are satisfied:

  • the i-th letter occurs in the string no more than ai times;
  • the number of occurrences of each letter in the string must be distinct for all the letters that occurred in the string at least once.
Input

The first line of the input contains a single integer n (2  ≤  n  ≤  26) — the number of letters in the alphabet.

The next line contains n integers ai (1 ≤ ai ≤ 109) — i-th of these integers gives the limitation on the number of occurrences of the i-th character in the string.

Output

Print a single integer — the maximum length of the string that meets all the requirements.

Sample test(s)
input
3
2 5 5
output
11
input
3
1 1 2
output
3
Note

For convenience let's consider an alphabet consisting of three letters: "a", "b", "c". In the first sample, some of the optimal strings are: "cccaabbccbb", "aabcbcbcbcb". In the second sample some of the optimal strings are: "acc", "cbc".


题意:

给出字母表前n位,每个字母最多使用ai次,且每个字母使用次数必须和其它字母使用次数严格不同,问这样能组成的字符串最长长度是多少

题解:

从大到小判断当前数字是否使用过即可

错误:

MAX在转移的时候没注意数值变化

#include<iostream>
#include<cstdio>
#include<cstdlib>
#include<algorithm>
#include<cstring>
#include<cmath>
#include<vector>
#include<queue>
using namespace std;

typedef long long LL;

LL num[30],ans = 0;
int n,MAX;

int main()
{
	#ifdef YZY
		   freopen("yzy.txt","r",stdin);
	#endif
	
	cin >> n; 
	for (int i = 0; i < n; i++) cin >> num[i];
	sort(num,num+n);
	ans = MAX = num[n-1];
	for (int i = n-2; i >= 0; i--) {
		if (num[i] < MAX) {
			ans += num[i];
			MAX = num[i];
		}
		else {
			MAX = MAX-1;
			ans += 1LL*MAX;
			if (!MAX) break;
		}
	}
	cout << ans;
	
	return 0;
}


C. Graph and String
time limit per test
2 seconds
memory limit per test
256 megabytes
input
standard input
output
standard output

One day student Vasya was sitting on a lecture and mentioned a string s1s2... sn, consisting of letters "a", "b" and "c" that was written on his desk. As the lecture was boring, Vasya decided to complete the picture by composing a graph G with the following properties:

  • G has exactly n vertices, numbered from 1 to n.
  • For all pairs of vertices i and j, where i ≠ j, there is an edge connecting them if and only if characters si and sj are either equal or neighbouring in the alphabet. That is, letters in pairs "a"-"b" and "b"-"c" are neighbouring, while letters "a"-"c" are not.

Vasya painted the resulting graph near the string and then erased the string. Next day Vasya's friend Petya came to a lecture and found some graph at his desk. He had heard of Vasya's adventure and now he wants to find out whether it could be the original graph G, painted by Vasya. In order to verify this, Petya needs to know whether there exists a string s, such that if Vasya used this s he would produce the given graph G.

Input

The first line of the input contains two integers n and m  — the number of vertices and edges in the graph found by Petya, respectively.

Each of the next m lines contains two integers ui and vi (1 ≤ ui, vi ≤ n, ui ≠ vi) — the edges of the graph G. It is guaranteed, that there are no multiple edges, that is any pair of vertexes appear in this list no more than once.

Output

In the first line print "Yes" (without the quotes), if the string s Petya is interested in really exists and "No" (without the quotes) otherwise.

If the string s exists, then print it on the second line of the output. The length of s must be exactly n, it must consist of only letters "a", "b" and "c" only, and the graph built using this string must coincide with G. If there are multiple possible answers, you may print any of them.

Sample test(s)
input
2 1
1 2
output
Yes
aa
input
4 3
1 2
1 3
1 4
output
No
Note

In the first sample you are given a graph made of two vertices with an edge between them. So, these vertices can correspond to both the same and adjacent letters. Any of the following strings "aa""ab""ba""bb""bc""cb""cc" meets the graph's conditions.

In the second sample the first vertex is connected to all three other vertices, but these three vertices are not connected with each other. That means that they must correspond to distinct letters that are not adjacent, but that is impossible as there are only two such letters: a and c.


题意:
Vasya写了一个仅由'a'、'b'、'c'组成的字符串,若字符串任意两个位置的字符相邻或相等(如'a'、'a'或'a'、'b')则在它们之间连一条边,这样就构成了一张图。现在给出这张图的边,问能否组成这张图,如果能,输出任意一种解。

题解:
对于'b',它与所有点都必须连边,那么所有度数为n-1的点的编号必须为'b'那么剩下的点用染色法即可

#include<iostream>
#include<cstdio>
#include<cstdlib>
#include<algorithm>
#include<cstring>
#include<cmath>
#include<vector>
#include<queue>
using namespace std;

const int maxn = 510;

vector <int> v1[maxn],v2[maxn];
queue <int> q;

bool EDG[maxn][maxn];
int du[maxn],mark[maxn],n,m;

int main()
{
	#ifdef YZY
		   freopen("yzy.txt","r",stdin);
	#endif
	
	cin >> n >> m;
	memset(EDG,0,sizeof(EDG));
	for (int i = 0; i < m; i++) {
		int x,y;
		scanf("%d%d",&x,&y);
		EDG[x][y] = EDG[y][x] = 1;
		++du[x]; ++du[y];
		v1[x].push_back(y);
		v1[y].push_back(x);
	}
	
	for (int i = 1; i <= n; i++)
		for (int j = 1; j <= n; j++)
			if (i != j && !EDG[i][j]) 
				v2[i].push_back(j);
				
	for (int i = 1; i <= n; i++)
		if (du[i] == n-1)
			mark[i] = 2;
			
	for (int i = 1; i <= n; i++)
		if (!mark[i]) {
			mark[i] = 1;
			q.push(i);
			while (!q.empty()) {
				int k = q.front(); q.pop();
				for (int j = 0; j < v1[k].size(); j++) {
					int to = v1[k][j];
					if (mark[to] == 2 || mark[to] == mark[k]) continue;
					if (mark[to] && mark[to] != mark[k]) {
						printf("No");
						return 0;
					}
					mark[to] = mark[k];
					q.push(to);
				}
				for (int j = 0; j < v2[k].size(); j++) {
					int to = v2[k][j];
					if (mark[to] == 2 || mark[to] == mark[k]) {
						printf("No");
						return 0;
					}
					if (mark[to] && mark[to] != mark[k]) continue;
					mark[to] = (mark[k] == 1)?3:1;
					q.push(to);
				}
			}
		}
	
	printf("Yes\n");
	for (int i = 1; i <= n; i++) printf("%c",mark[i]-1+'a');
	return 0;
}


D. Array GCD
time limit per test
3 seconds
memory limit per test
256 megabytes
input
standard input
output
standard output

You are given array ai of length n. You may consecutively apply two operations to this array:

  • remove some subsegment (continuous subsequence) of length m < n and pay for it m·a coins;
  • change some elements of the array by at most 1, and pay b coins for each change.

Please note that each of operations may be applied at most once (and may be not applied at all) so you can remove only one segment and each number may be changed (increased or decreased) by at most 1. Also note, that you are not allowed to delete the whole array.

Your goal is to calculate the minimum number of coins that you need to spend in order to make the greatest common divisor of the elements of the resulting array be greater than 1.

Input

The first line of the input contains integers na and b (1 ≤ n ≤ 1 000 000, 0 ≤ a, b ≤ 109) — the length of the array, the cost of removing a single element in the first operation and the cost of changing an element, respectively.

The second line contains n integers ai (2 ≤ ai ≤ 109) — elements of the array.

Output

Print a single number — the minimum cost of changes needed to obtain an array, such that the greatest common divisor of all its elements is greater than 1.

Sample test(s)
input
3 1 4
4 2 3
output
1
input
5 3 2
5 17 13 5 6
output
8
input
8 3 4
3 7 5 4 3 12 9 4
output
13
Note

In the first sample the optimal way is to remove number 3 and pay 1 coin for it.

In the second sample you need to remove a segment [17, 13] and then decrease number 6. The cost of these changes is equal to2·3 + 2 = 8 coins.


题意:

有一个长度为n的数组,可进行两种操作:

1.删去其中一段连续的数字,并付出n*a的代价

2.将其中一个元素+1或-1并付出b的代价

1操作只能进行一次,2操作对每个数都只能进行一次,问最少付出多少代价能使整个数组最大公约数大于1


题解:

由题意,显然至少数组会留下第一个数或最后一个数

那么num[1]、num[1]+-1、num[n]、num[n]+-1这六个数中必有一个存在题解数组里,将他们分解质因数,那么题解数组里至少含有这些质因数的其中一个,设为k,可对每个数判断是否整除k(略)

记f[i]为从左到右进行到i位置,每次只进行2操作或不进行的代价

记g[j]为从右到左进行到j位置,同上

那么答案为f[i]+g[j]+(j-i-1)*a

即(f[i]-i*a) + (g[j]+j*a)-a

对于括号内的数维护最小值,暴力forforfor...

对于质数用Miller_Rubin(好像是这个名?费马小定理+二次判定定理)

#include<iostream>
#include<cstdio>
#include<cstdlib>
#include<algorithm>
#include<cstring>
#include<cmath>
#include<vector>
#include<queue>
#include<map>
using namespace std;

typedef long long LL;
const int maxn = 1E6 + 10;

map <int,bool> M;
LL n,a,b,num[maxn],f[maxn],g[maxn],mi[maxn],mi2[maxn],ans = 1E17;
bool flag;

void cal(LL x)
{
	int R = 0;
	f[0] = mi[0] = g[n+1] = 0;
	mi2[n+1] = 1LL*(n+1)*a;
	for (int i = 1; i <= n; i++) {
		LL sum;
		if (num[i] % x == 0) sum = 0;
		else if ((num[i]+1) % x == 0) sum = b;
		else if ((num[i]-1) % x == 0 && num[i]-1>0) sum = b;
		else {
			R = i;
			break;
		}
		f[i] = f[i-1] + sum;
		mi[i] = min(f[i]-1LL*a*1LL*i,mi[i-1]);
		if (i == n) ans = min(ans,f[i]);
	}
	if (R) for (int i = R; i <= n; i++) mi[i] = mi[i-1];
	
	LL L = 0;
	for (int i = n; i; i--) {
		LL sum; 
		if (num[i] % x == 0 && num[i]) sum = 0;
		else if ((num[i]+1) % x == 0 && num[i]+1>0) sum = b;
		else if ((num[i]-1) % x == 0 && num[i]-1>0) sum = b;
		else {
			L = i;
			break;
		}
		g[i] = g[i+1]+sum;
		mi2[i] = min(mi2[i+1],g[i]+1LL*a*1LL*i);
		if (i == 1) ans = min(ans,g[i]);
	}
	if (L) for (int i = L; i; i--) mi2[i] = mi2[i+1];
	
	for (int i = 1; i <= n+1; i++) ans = min(ans,mi[i-1]+mi2[i]-a);
}

LL ksm(LL a,LL x,LL y)
{
	if (x == 0) return 1;
	LL r = ksm(a,x/2,y);
	LL ret = r*r%y;
	if (ret == 1 && r != 1 && r != y-1) flag = 0;
	if (x&1) ret = ret*a%y;
	return ret;
}

bool M_B(LL x)
{
	for (int i = 1; i <= 10; i++) {
		flag = true;
		LL a = rand()%(x-1)+1;
		LL ret = ksm(a,x-1,x);
		if (ret != 1 || !flag) return false;
	}
	return true;
}

void solve(LL now)
{
	if (now <= 1) return;
	if (M_B(now) && !M[now]) 
		cal(now),M[now] = 1;
	else {
		LL SQRT = sqrt(now);
		for (int i = 2; i <= SQRT; i++)
			if (now % i == 0) {
				if (M_B(i) && !M[i])
					cal(i),M[i] = 1;
				LL j = now/i;
				if (M_B(j) && !M[j])
				 	cal(j),M[j] = 1;
			}
	}
}

int main()
{
	#ifdef YZY
		   freopen("yzy.txt","r",stdin);
	#endif
	
	cin >> n >> a >> b;
	for (int i = 1; i <= n; i++) scanf("%I64d",&num[i]);
	solve(num[1]); 
	solve(num[1]-1); 
	solve(num[1]+1);
	solve(num[n]); 
	solve(num[n]-1); 
	solve(num[n]+1);
	cout << ans;
	
	return 0;
}



  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值