1. 程式人生 > >電子科技第十屆ACM趣味程式設計競賽第三場(正式賽)官方題解

電子科技第十屆ACM趣味程式設計競賽第三場(正式賽)官方題解

A 秦皇炒飯

source: Pxt

偶數不滿足兩兩互質,因此最少要分n/2組。相鄰的兩個數2k與2k+1一定互質可以分為1組,編號為1的可以加入任意組。

因此,$$ ans=\left\{ \begin{aligned} 1 && n=1 \\ n/2 && n>1 \end{aligned} \right. $$.

#include<cstdio>
using namespace std;
int i,n;
int main()
{
   scanf("%d",&n);
   if(n>1) n/=2;
   for(i=1;i<=n;++i) printf("Wed.Strong");
   return 0;
}

B 摩天樂

source: Range

對於輸入在同一層的特判。如果一開始就在電梯範圍,就直接到同一行再走。否則分左邊下去和右邊下去的情況,可以知道肯定是從$L$

$R$的位置下去,所以兩邊都算一下取個最小值就行。

該題測試組數T最大值實際上為1000,這是我們的過失,在這裡向開陣列存資料導致RE的同學道個歉了。

#include <iostream>
#include <stdio.h>
#include <algorithm>
#include <math.h>
using namespace std;

long long n, m, l, r, a, b, c, d;
int main()
{
  int T; cin >> T;
  while (T--)
  {
    cin >> n >> m >> l >> r >> a >> b >> c >> d;
    if (a == c)
    {
      cout << abs(b-d) << endl;
      continue;
    }
    long long ans = 0;
    if (b <= l || b >= r)
    {
      ans = abs(a-c) + abs(b-d);
    }
    else
    {
      long long cost1 = b-l+abs(d-l)+abs(a-c);
      long long cost2 = r-b+abs(r-d)+abs(a-c);
      ans = min(cost1, cost2);
    }
    cout << ans << endl;
  }
  return 0;
}

C Akane

source: meltout

記錄%m餘0,1,2,....m-1的元素個數

重排列後儘量使餘數和為m的元素相鄰,假設兩者個數分別為a,b,若a=b,則最多隻能構成a$\times$2-1對,否則,可以通過將個數更多的元素放在外圍,構造min(a,b)$\times$2對

特別地,%m餘0的元素可以不計順序直接放在一起,假設元素個數為a,可構成a-1對

若m為偶數,則%m餘m/2的元素也可以直接放在一起,對答案貢獻同上

需要注意a=0時特判,以免對答案做負貢獻
 

#include <cstdio>
#include <algorithm>

using namespace std;

const int maxN=1e6+5;

int a[maxN];

int main()
{
    int k,n,m,x;
    scanf("%d%d",&n,&m);
    for(k=0;k<n;k++)
    {
        scanf("%d",&x);
        a[x%m]++;
    }
    int ans=max(0,a[0]-1);
    for(k=1;k<(m+1)/2;k++)
    {
        ans+=min(a[k],a[m-k])*2;
        if(a[k]==a[m-k] && a[k]!=0)ans--;
    }
    if(m%2==0)ans+=max(0,a[m/2]-1);
    printf("%d",ans);
    return 0;
}

D 強哥打電話

source: zhsq11, forgottencsc

注意到情侶隊友打電話的時間是順序給出的,所以順序檢查即可。

題目要求情侶隊友的電話時間是左閉右開的,也就是說掛下電話的一瞬間是可以打通電話的。

因此暴力檢查一下每段強哥打電話的區間能不能打通就行了。

關於這個時間表示法的處理辦法:可以寫一個將給出的時間轉換成秒數的函式,這樣就比較好進行處理了。

#include <cstdio>
#include <iostream>
using namespace std;

const int Time_Max = 24 * 60 * 60; 
const int MAXN = 100 + 10;
int From[MAXN], To[MAXN]; int N;
int Qiang_Time, Qiang_Interval, Qiang_Wait;

int Time_Conversion()
{
	int hour, min, sec;
	scanf("%d:%d:%d", &hour, &min, &sec);
	return hour * 60 * 60 + min * 60 + sec;
}

void output(int seconds)
{
	int hour = seconds / (60 * 60);
	int min = (seconds - hour * (60 * 60)) / 60;
	int sec = seconds - hour * (60 * 60) - min * 60;
	if(seconds >= Time_Max)
		printf("-1\n");
	else
		printf("%02d:%02d:%02d\n", hour, min, sec);
	exit(0);
}

void read()
{
	cin >> N;
	int h1, m1, s1, h2, m2, s2;
	for(int i = 0;i < N; ++i)
	{
		From[i] = Time_Conversion();
		To[i] = Time_Conversion();
	}
	Qiang_Time = Time_Conversion();
	Qiang_Interval = Time_Conversion();
	Qiang_Wait = Time_Conversion();
	return;
}

void find()
{
	for(int i = 0;i < N; ++i)
	{	
		while(Qiang_Time <= To[i])
		{
			if(Qiang_Time < From[i])
				output(Qiang_Time);
			if(Qiang_Time + Qiang_Wait >= To[i])
				output(To[i]);
			Qiang_Time += Qiang_Interval;
		}
	}
	output(Qiang_Time);
	return;
}

int main()
{
	read(); find();
	return 0;
}

E  暴擊貓

source: moe

$f[i][j]$表示第j次攻擊前有i 層buff的概率,在無限次攻擊後i將會無意義,也就是說$f[i]$會收斂。所以用$f[i]$表示無限次數攻擊後有i層buff的概率。$f[0]$顯然為p,$f[1]$為p*(1-p),以此類推,$f[n-1]=p*(1-p)^{n-1}$ 。注意因為有buff層數上限所以$f[n]=(f[n-1]+f[n])*(1-p)$,解得$f[n]=(1-p)^n$ 。剩下的就是算期望再作比了。

另解,直接暴力模擬,用隨機函式來隨機是否暴擊然後模擬過程,算出兩種總傷害然後作比。

#include <bits/stdc++.h>
using namespace std;

int main()
{
	double p, k, a;
	int n;
	scanf("%lf%lf%lf%d", &p, &k, &a, &n);
	double init = p * k + (1 - p) * 100;
	double res = 0, base = p;
	for (int i = 1; i <= n; i++)
	{
		res += base * (k + a * (i - 1)) * p;
		base = base * (1 - p);
	}
	res += base * (k + n * a);
	res += (1 - p) * 100;
	printf("%.3lf", res / init);
	return 0;
}