AtCoder Beginner Contest 367

AtCoder Beginner Contest 367

A - Shout Everyday

Problem Statement

In the Kingdom of AtCoder, residents are required to shout their love for takoyaki at \(A\) o'clock every day.

Takahashi, who lives in the Kingdom of AtCoder, goes to bed at \(B\) o'clock and wakes up at \(C\) o'clock every day (in the \(24\)-hour clock). He can shout his love for takoyaki when he is awake, but cannot when he is asleep. Determine whether he can shout his love for takoyaki every day. Here, a day has \(24\) hours, and his sleeping time is less than \(24\)​ hours.

在 AtCoder 王国,居民们每天都要在 \(A\) 点大声喊出他们对章鱼烧的热爱。

住在 AtCoder 王国的高桥每天 \(B\) 点睡觉, \(C\) 点起床( \(24\) 小时钟)。他醒着的时候可以喊出对章鱼烧的爱,但睡着的时候却不能。判断他是否每天都能喊出对章鱼烧的爱。这里,一天有 \(24\) 小时,他的睡眠时间小于 \(24\) 小时。

Constraints

  • \(0\leq A,B,C\lt 24\)
  • \(A\), \(B\), and \(C\) are pairwise different.
  • All input values are integers.

Input

The input is given from Standard Input in the following format:

$A$ $B$ $C$

Output

Print Yes if Takahashi can shout his love for takoyaki every day, and No otherwise.

Sample Input 1

21 8 14

Sample Output 1

Yes

Takahashi goes to bed at \(8\) o'clock and wakes up at \(14\) o'clock every day. He is awake at \(21\) o'clock, so he can shout his love for takoyaki every day. Therefore, print Yes.

Solution

注意睡眠时间从晚上到早上造成起始时间和终止时间的先小后大的判断

Code
#include<bits/stdc++.h>
#define int long long

using namespace std;

typedef pair<int, int> PII;

const int INF = 0x3f3f3f3f;

int a, b, c;

void solve()
{
    cin >> a >> b >> c;
    if (b < c)
    {
    	if (a >= b && a <= c) cout << "No" << endl;
    	else cout << "Yes" << endl;
    }
    else
    {
    	if (a >= b || a <= c) cout << "No" << endl;
    	else cout << "Yes" << endl;
    }
}

signed main()
{
    ios::sync_with_stdio(0);
    cin.tie(0);
    cout.tie(0);
    
    solve();

    return 0;
}

B - Cut .0

Problem Statement

A real number \(X\) is given to the third decimal place.

Print the real number \(X\) under the following conditions.

  • The decimal part must not have trailing 0s.
  • There must not be an unnecessary trailing decimal point.

Constraints

  • \(0 \le X \lt 100\)
  • \(X\) is given to the third decimal place.

Input

The input is given from Standard Input in the following format:

$X$

Output

Output the answer.

Sample Input 1

1.012

Sample Output 1

1.012

1.012 can be printed as it is.

Solution

倒序遍历字符串判断尾数0的个数,在输出是不输出尾数0即可

Code
#include<bits/stdc++.h>
#define int long long

using namespace std;

typedef pair<int, int> PII;

const int INF = 0x3f3f3f3f;

string str;

void solve()
{
    cin >> str;
    int cnt = str.size();
    for (int i = str.size() - 1; i >= 0; i -- )
    {
    	if (str[i] == '.') 
    	{
    		cnt -- ;
    		break;
    	}
    	if (str[i] != '0') break;
    	cnt -- ;
    }
    for (int i = 0; i < cnt; i ++ ) cout << str[i];
    cout << endl;
}

signed main()
{
    ios::sync_with_stdio(0);
    cin.tie(0);
    cout.tie(0);
    
    solve();

    return 0;
}

C - Enumerate Sequences

Problem Statement

Print all integer sequences of length \(N\) that satisfy the following conditions, in ascending lexicographical order.

  • The \(i\)-th element is between \(1\) and \(R_i\), inclusive.
  • The sum of all elements is a multiple of \(K\).

What is lexicographical order for sequences? A sequence \(A = (A_1, \ldots, A_{|A|})\) is lexicographically smaller than \(B = (B_1, \ldots, B_{|B|})\) if either 1. or 2. below holds:

  1. \(|A|\lt|B|\) and \((A_{1},\ldots,A_{|A|}) = (B_1,\ldots,B_{|A|})\).
  2. There exists an integer \(1\leq i\leq \min\{|A|,|B|\}\) such that both of the following are true:
    • \((A_{1},\ldots,A_{i-1}) = (B_1,\ldots,B_{i-1})\)
    • \(A_i \lt B_i\)

按升序排列,打印所有满足以下条件的长度为 \(N\) 的整数序列。

  • \(i\) 个元素介于 \(1\)\(R_i\) 之间。
  • 所有元素之和是 \(K\) 的倍数。

什么是序列的词典顺序?如果下面的 1. 或 2. 成立,那么序列 \(A = (A_1, \ldots, A_{|A|})\) 在词法上**小于 \(B = (B_1, \ldots, B_{|B|})\)

  1. \(|A|\lt|B|\)\((A_{1},\ldots,A_{|A|}) = (B_1,\ldots,B_{|A|})\)
  2. 存在一个整数 \(1\leq i\leq \min\{|A|,|B|\}\) ,使得下面两个条件都成立:
    • \((A_{1},\ldots,A_{i-1}) = (B_1,\ldots,B_{i-1})\)
    • \(A_i \lt B_i\)

Constraints

  • All input values are integers.
  • \(1 \le N \le 8\)
  • \(2 \le K \le 10\)
  • \(1 \le R_i \le 5\)

Input

The input is given from Standard Input in the following format:

\(N\) \(K\)
\(R_1\) \(R_2\) \(\dots\) \(R_N\)

Output

Print the answer in the following format, where \(X\) is the number of sequences to print, the \(i\)-th of which is \(A_i=(A_{i,1},A_{i,2},\dots,A_{i,N})\):

\(A_{1,1}\) \(A_{1,2}\) \(\dots\) \(A_{1,N}\)
\(A_{2,1}\) \(A_{2,2}\) \(\dots\) \(A_{2,N}\)
\(\vdots\)
\(A_{X,1}\) \(A_{X,2}\) \(\dots\) \(A_{X,N}\)

Sample Input 1

3 2
2 1 3

Sample Output 1

1 1 2
2 1 1
2 1 3

There are three sequences to be printed, which are \((1,1,2),(2,1,1),(2,1,3)\) in lexicographical order.

Solution

这道题要求用词典序输出,并且数据范围很小,我们首先考虑dp和dfs,由于要求词典序,我们发现深度优先搜索是最合适的。

对每一位数字在满足介于1~r[i]的条件下枚举搜索,当当前字串长度已经达到n时,判断是否满足是k的倍数的要求,若满足,输出,否则返回。

我们容易发现若对于三个数字组成的串,肯定是优先搜索1 1 1 再搜索1 1 2,从低位到高位改变,满足词典序。

Code
#include<bits/stdc++.h>
#define int long long

using namespace std;

typedef pair<int, int> PII;

const int INF = 0x3f3f3f3f;
const int N = 10;


int n, k;
int r[N];
int ans[N];

void dfs(int depth, int ans[])
{
	//cout << n << endl;
	if (depth == n)
	{
		//cout << n << endl;
		int res = 0;
		for (int i = 0; i < n; i ++ )
		{
			res += ans[i];
		}
		if (res % k == 0)
		{
			for (int i = 0; i < n; i ++ )
			{
				cout << ans[i] << " ";
			}
			cout << endl;
		}
		
		return;
	}
	for (int i = 1; i <= r[depth]; i ++ )
	{
		ans[depth] = i;
		dfs(depth + 1, ans);
	}
	return;
}

void solve()
{
    cin >> n >> k;
    for (int i = 0; i < n; i ++ ) cin >> r[i];
    dfs(0, ans);

}

signed main()
{
    ios::sync_with_stdio(0);
    cin.tie(0);
    cout.tie(0);
    
    solve();

    return 0;
}

D - Pedometer

Problem Statement

There are \(N\) rest areas around a lake.
The rest areas are numbered \(1\), \(2\), ..., \(N\) in clockwise order.
It takes \(A_i\) steps to walk clockwise from rest area \(i\) to rest area \(i+1\) (where rest area \(N+1\) refers to rest area \(1\)).
The minimum number of steps required to walk clockwise from rest area \(s\) to rest area \(t\) (\(s \neq t\)) is a multiple of \(M\).
Find the number of possible pairs \((s,t)\).

一个湖周围有 \(N\) 个休息区。
休息区按顺时针顺序编号为 \(1\)\(2\) 、......、 \(N\)
从休息区 \(i\) 顺时针走到休息区 \(i+1\) 需要 \(A_i\) 步(其中休息区 \(N+1\) 指的是休息区 \(1\) )。
从休息区 \(s\) 顺时针走到休息区 \(t\)\(s \neq t\) )所需的最小步数是 \(M\) 的倍数。
\((s,t)\) 可能的对数。

Constraints

  • All input values are integers
  • \(2 \le N \le 2 \times 10^5\)
  • \(1 \le A_i \le 10^9\)
  • \(1 \le M \le 10^6\)

Input

The input is given from Standard Input in the following format:

\(N\) \(M\)
\(A_1\) \(A_2\) \(\dots\) \(A_N\)

Output

Print the answer as an integer.

Sample Input 1

4 3
2 1 4 3

Sample Output 1

4
  • The minimum number of steps to walk clockwise from rest area \(1\) to rest area \(2\) is \(2\), which is not a multiple of \(3\).
  • The minimum number of steps to walk clockwise from rest area \(1\) to rest area \(3\) is \(3\), which is a multiple of \(3\).
  • The minimum number of steps to walk clockwise from rest area \(1\) to rest area \(4\) is \(7\), which is not a multiple of \(3\).
  • The minimum number of steps to walk clockwise from rest area \(2\) to rest area \(3\) is \(1\), which is not a multiple of \(3\).
  • The minimum number of steps to walk clockwise from rest area \(2\) to rest area \(4\) is \(5\), which is not a multiple of \(3\).
  • The minimum number of steps to walk clockwise from rest area \(2\) to rest area \(1\) is \(8\), which is not a multiple of \(3\).
  • The minimum number of steps to walk clockwise from rest area \(3\) to rest area \(4\) is \(4\), which is not a multiple of \(3\).
  • The minimum number of steps to walk clockwise from rest area \(3\) to rest area \(1\) is \(7\), which is not a multiple of \(3\).
  • The minimum number of steps to walk clockwise from rest area \(3\) to rest area \(2\) is \(9\), which is a multiple of \(3\).
  • The minimum number of steps to walk clockwise from rest area \(4\) to rest area \(1\) is \(3\), which is a multiple of \(3\).
  • The minimum number of steps to walk clockwise from rest area \(4\) to rest area \(2\) is \(5\), which is not a multiple of \(3\).
  • The minimum number of steps to walk clockwise from rest area \(4\) to rest area \(3\) is \(6\), which is a multiple of \(3\).

Therefore, there are four possible pairs \((s,t)\).

Solution

根据题意,我们走到所有景区都是按照顺时针走,当走到第n个景点时,我们下一步是走到景点1,需要\(A_n\)步,也就是说我们在以1~n构成的圈内行走,那如何在代码中实现这个圈呢,我们将\(A_1 \dots A_n\)拉长两倍,这样也就可以实现景点都能走到自己的前一个景点。

我们利用样例举例

将景点1 2 3 4,拉长, 1 2 3 4 1 2 3 4,每个景点距离\(1_{2} 2_{1} 3_{4} 4_{3} 1_2 2_{1} 3_{4} 4\),这样我们就可以实现从2走到1,从3走到2 1,从4走到3 2 1,从而与环等价。

答案要求配对景点对数,配对景点(x, y)是指从x走到y需要的步数mod m等于0。再本题条件下,显然(x, y)与(y, x)是不同的。

那我们怎么找到配对景点,一般做法是将每两个点之间配对,时间复杂度是\(O(n^2)\)级,在本题数据范围下肯定会超时。

近一步,我们想到可以利用区间和的思路来判断两个点之间的步数,这里有个很妙的思路,如果我们对每一个数求前缀和,将其mod上m,若两个数的前缀和s[x] mod m == s[y] mod m,那么点n和m是配对景点。

推导一下s[x] mod m - s[y] mod m == 0 → (s[x] - s[y]) mod m == 0,即两个点之间的步数满足mod m = 0的要求。

当走到i>n时,为什么要进行操作cnt[s[i - n] % m] -- ; ,由于不能从i走到i+n(不能走到自身) 当遍历到i+n时,减去i点的记录cnt[s[i] % m]后再记录答案,相当于是滑动窗口的概念,对于1 2 3 4 1 2 3 4,当我们进入拉长点,要从2走到1 我们的路线是2 3 4 1,不会经过i=1的点了,所以要将其s[i - n] % m从cnt里删去,相当于每次枚举的i是路径终点。而且在拉长点内若不删除,假如(1, 2)配对,

在1 2 3 4 1 2 3 4,这里1 2会重复配对。也就是说拉长点只能用来处理倒着走的情况。

在i=n* 2 -1时,也就是第二个3,他只能看能否与cnt[s[i=4] % m]配对,在i=n*2时,这个点是没用的,不能与任何点进行配对,cnt数组内已经全部清除了.

Code
#include<bits/stdc++.h>
#define int long long

using namespace std;

typedef pair<int, int> PII;

const int INF = 0x3f3f3f3f;
const int N = 2e5 + 10;
const int M = 1e6 + 10;

int n, m, ans;
int a[N * 2], s[N * 2], cnt[M]; //由于要将数组延申两倍,所以是2*n,我们用cnt数组记录两个mod m后值相同的前缀和 数组范围上限是m的上限

void solve()
{
    cin >> n >> m;
    for (int i = 1; i <= n; i ++ ) 
    {
        cin >> a[i];
        a[n + i] = a[i]; //给拉长的景点赋值
    }
    for (int i = 1; i <= 2 * n; i ++ )
    {
        s[i] = s[i - 1] + a[i - 1]; //求前缀和,由于走到每个景点依赖的是前面景点的a[i],所以这里加上的是a[i - 1]
    }

    for (int i = 1; i < 2 * n; i ++ )
    {
    	if (i <= n)
        {
            ans += cnt[s[i] % m]; //如果当前点的前缀和与前面点相同,此时cnt[]=1加进答案,如果不相同cnt[]=0也不会有影响
            cnt[s[i] % m] ++ ; //mod m后值等于s[i] % m的景点个数+1
        }
        else
        {
            cnt[s[i - n] % m] -- ; //由于不能从i走到i+n(不能走到自身) 当遍历到i+n时,减去i点的记录cnt[s[i] % m]后再记录答案
            ans += cnt[s[i] % m]; //若有模值相同的点记录进答案
        }                         //这里不进行cnt[s[i] % m] ++操作的原因是只处理倒着走的情况,只能与1~n的点进行配对
    }
    cout << ans << endl;

}

signed main()
{
    ios::sync_with_stdio(0);
    cin.tie(0);
    cout.tie(0);
    
    solve();

    return 0;
}

E - Permute K times

Problem Statement

You are given a sequence \(X\) of length \(N\) where each element is between \(1\) and \(N\), inclusive, and a sequence \(A\) of length \(N\).
Print the result of performing the following operation \(K\) times on \(A\).

  • Replace \(A\) with \(B\) such that \(B_i = A_{X_i}\).

给你一个长度为 \(N\) 的序列 \(X\) ,其中每个元素都在 \(1\)\(N\) 之间(包括首尾两个元素),以及一个长度为 \(N\) 的序列 \(A\)
打印在 \(A\) 上执行以下操作 \(K\) 次的结果。

  • \(B\) 替换 \(A\) ,使得 \(B_i = A_{X_i}\) .

Constraints

  • All input values are integers.
  • \(1 \le N \le 2 \times 10^5\)
  • \(0 \le K \le 10^{18}\)
  • \(1 \le X_i \le N\)
  • \(1 \le A_i \le 2 \times 10^5\)

Input

The input is given from Standard Input in the following format:

\(N\) \(K\)
\(X_1\) \(X_2\) \(\dots\) \(X_N\)
\(A_1\) \(A_2\) \(\dots\) \(A_N\)

Output

Let \(A'\) be the sequence \(A\) after the operations. Print it in the following format:

\(A'_1\) \(A'_2\) \(\dots\) \(A'_N\)

Sample Input 1

7 3
5 2 6 3 1 4 6
1 2 3 5 7 9 11

Sample Output 1

7 2 3 5 1 9 3

In this input, \(X=(5,2,6,3,1,4,6)\) and the initial sequence is \(A=(1,2,3,5,7,9,11)\).

  • After one operation, the sequence is \((7,2,9,3,1,5,9)\).
  • After two operations, the sequence is \((1,2,5,9,7,3,5)\).
  • After three operations, the sequence is \((7,2,3,5,1,9,3)\).

Solution

以前没有做过类似的题目,题意就是每次操作将\(A_i 更新为 A_{X_i}\),在K次操作后输出A数组。

我们将\(X_i\)数组建图,它指引了一次操作后第i个点的值该被替换为第\(X_i\)个点的值

利用样例举例,箭头是根据X[]数组算出的第i号点应该被替换为的点,例如第1次操作 7号点应该被替换为6号点的值,第2次操作就被替换为4号点的值,我们只要沿着有向路径一直走下去,一步就对应一次操作。但是由于题目K的上限为\(10^{18}\),所以这么做肯定会超时,我们采用二进制优化,例如将K次操作分解为其二进制表示,则最多进行60次操作,举个例子,若K=12即 0b 1100,则我们需要进行\(2^3+2^2\)次操作,这样我们先预处理出\(2^k\)次操作的目标替换元素,再将k分解为二进制表示,\(10^{18}\)小于\(2^{60}\),所以在64次操作内一定能完成所有操作。

我们利用to[i] [0]表示第i号元素走\(2^0\)步后替换的目标序号,例如to[7] [0]=6

则想走\(2^j\)步,有状态转移方程\(to[i][j] = to[to[i][j-1]][j-1]\),想走\(2^j\)步,可以用先走\(2^{j-1}\)步走到后再在\(2^{j-1}\)的基础上再走\(2^{j-1}\)步,就可以走\(2^j\)步,这样把60位的情况都先预处理出来。

Code
#include<bits/stdc++.h>
#define int long long

using namespace std;

typedef pair<int, int> PII;

const int INF = 0x3f3f3f3f;
const int N = 2e5 + 10;

int n, k;
int a[N], to[N][64];

void solve()
{
    cin >> n >> k;
    for (int i = 1; i <= n; i ++ ) cin >> to[i][0]; //X[]数组即是走一步到达的点即to[i][0]
    for (int i = 1; i <= n; i ++ ) cin >> a[i];

    //预处理所有点在经过最多2^60步后到达的目标点
    for (int j = 1; j <= 60; j ++ )
    	for (int i = 1; i <= n; i ++ )
    	{
    		to[i][j] = to[to[i][j - 1]][j - 1]; //关键转移   注意j在外层 i在内层   因为对每一个i都要用到to[to[i][j - 1]][j - 1]内层的to[i][j - 1]作为新的i,此时将j放在外层,这里的to[x][j - 1]的结果就已经提前算出
    	}

    for (int i = 1; i <= n; i ++ )
    {
    	int p = i;
    	for (int j = 0; j <= 60; j ++ )
    	{
    		if ((k >> j) & 1) p = to[p][j]; //如果K的当前位为1,p更新为转移2^j后的目标点,方便下次循环继续转移,这样等效于将k内为1的位全部转移,每次循环转移一次
    	}
    	cout << a[p] << " "; //全部位操作结束后输出
    }
    cout << endl;
}

signed main()
{
    ios::sync_with_stdio(0);
    cin.tie(0);
    cout.tie(0);
    
    solve();

    return 0;
}
posted @ 2024-08-18 15:24  MsEEi  阅读(27)  评论(0)    收藏  举报