SDUT 2021 Spring Individual Contest(for 20) - 8补题

20 篇文章 0 订阅
10 篇文章 1 订阅

Magical Indices

Alaa sometimes feels bored at work, so at such times she starts playing with a beautiful array a consisting of n integers a1, a2, …, an.
Alaa starts counting the number of magical indices in the array a. An index x is said to be magical if it satisfying the following rules:
1 < x < n
ay ≤ ax, for each y (1 ≤ y < x).
ax ≤ az, for each z (x < z ≤ n).
Can you help Alaa by counting the number of magical indices in the array a.
Input
The first line contains an integer T, where T is the number of test cases.
The first line of each test case contains an integer n (1 ≤ n ≤ 106), where n is the size of the array a.
The second line of each test case contains n integers a1, a2, …, an (1 ≤ ai ≤ 106), giving the array a.
Output
For each test case, print a single line containing the number of magical indices in the array a.
Example
Input

2
8
2 1 3 4 6 5 7 9
6
4 2 7 9 8 10
Output
3
1

关键点就在于如何不用两重循环的一次一次遍历的方法下,来比较a[i]与左边的数与右边的数的大小关系,单纯的两重循环会TLE。
所以可以开个数组,来记录a[i]左边最大的数,和a[i]右边最小的数。
只要a[i]>=左边最大的数且a[i]<=右边最小的数,那肯定符合题意。

#include<iostream>
#include<cstring>
#include<algorithm>
using namespace std;
const int N=1000010;
int a[N],pre[N],aft[N]; //开两个数组,pre记录从开头到当前位置的最大值 
int main()               //aft记录从末端到当前位置的最小值 
{
	int t;
	scanf("%d",&t);
	while(t--)
	{
		int n;
		scanf("%d",&n);
		for(int i=1;i<=n;i++)
		scanf("%d",&a[i]);
		pre[1]=a[1];  
		aft[n]=a[n];
		for(int i=2;i<=n;i++)
		{
			if(a[i]>pre[i-1]) //如果当前值比pre[i-1]中的值还大,根据队pre数组的定义,pre[i]就改换成a[i] 
			pre[i]=a[i];
			else              //否则就仍记录的是前面pre[i-1]的值,因为a[i]不够大,不是从开头到当前位置的最大值 
			pre[i]=pre[i-1];
		}
		for(int i=n-1;i>=1;i--)
		{
			if(a[i]<aft[i+1]) //与上面同理,不过就是求从末端到当前位置的最小值 
			aft[i]=a[i];
			else aft[i]=aft[i+1];
		}
		int count=0;
		for(int i=2;i<n;i++)    //只要a[i]比它前面的的数的最大值还大或等于那就肯定比前面其他小的数还大 
		{                                    //只要a[i]比他后面的数的最小值还小或者等于,那就肯定比那些更大的数还小 
			if(a[i]>=pre[i-1]&&a[i]<=aft[i+1])
			count++;                     //符合条件就计数一次 
		}
		printf("%d\n",count);
	 } 
	 return 0;
}

Array Reconstructing

You are given an array a consisting of n elements a1, a2, …, an. Array a has a special property, which is:

ai = (ai - 1 + 1) % m, for each i (1 < i ≤ n)
You are given the array a with some lost elements from it, each lost element is replaced by -1. Your task is to find all the lost elements again, can you?

Input
The first line contains an integer T, where T is the number of test cases.

The first line of each test case contains two integers n and m (1 ≤ n ≤ 1000) (1 ≤ m ≤ 109), where n is the size of the array, and m is the described modulus in the problem statement.

The second line of each test case contains n integers a1, a2, …, an ( - 1 ≤ ai < m), giving the array a. If the ith element is lost, then ai will be -1. Otherwise, ai will be a non-negative integer less than m.

It is guaranteed that the input is correct, and there is at least one non-lost element in the given array.

Output
For each test case, print a single line containing n integers a1, a2, …, an, giving the array a after finding all the lost elements.

It is guaranteed that an answer exists for the given input.

Example
Input

4
5 10
1 2 3 4 5
4 10
7 -1 9 -1
6 7
5 -1 -1 1 2 3
6 10
5 -1 7 -1 9 0
Output
1 2 3 4 5
7 8 9 0
5 6 0 1 2 3
5 6 7 8 9 0
没什么说的,就是遍历,找第一个不为-1的数,然后分别向两边用公式把值推导出来。特别注意的时,如果当前的这个值是0的话,那它前面的值就为m-1,否则就是a[j+1]-1。往后退推导的话,没什么好注意的,就按照公式来即可。

#include<iostream>
#include<cstring>
#include<algorithm>
using namespace std;
#define ll long long
ll a[2020];
int main()
{
	int t;
	scanf("%d",&t);
	while(t--)
	{
		memset(a,0,sizeof(a));
		ll m;
		int n;
		scanf("%d%lld",&n,&m);
		for(int i=1;i<=n;i++)
		scanf("%lld",&a[i]);
		for(int i=1;i<=n;i++)
		{
			if(a[i]!=-1)
			{
				for(int j=i-1;j>=1;j--)
				{
					if(a[j+1]==0)
					a[j]=m-1;
					else a[j]=a[j+1]-1;
				}
				for(int k=i+1;k<=n;k++)
				{
					a[k]=(a[k-1]+1)%m;
				}
			}
			if(a[i]==-1) continue;
			else break;
		}
		for(int i=1;i<=n;i++)
		{
			if(i==1) printf("%lld",a[i]);
			else printf(" %lld",a[i]);
		}
		printf("\n");
	}
	return 0;
}

The Crazy Jumper

Universal Studios Theme Parks announced a new game named The Crazy Jumper, that is targeted problem solving fans.

In this game, there are n large boxes numbered from 1 to n from left to right, such that the ith box (1 < i ≤ n) is located to the right of the (i - 1)th box. Each box has a color, such that ci is the color of the ith box.

The goal of the game is to reach the nth box starting from the 1st box, by jumping between the boxes. The player can do one of the following jumps:

Jump one box to the right.
If the player stands at a box of color x, he/she can jump to the closest box of color x that is to the right of him, if such box exist.
Ziad will be the first one to play the game, but he is not sure whether the game is amusing or not, so he wants to finish it with the minimum number of jumps. Can you help Ziad by telling him what is the minimum number of required jumps to reach the nth box stating from the 1st box?

Input
The first line contains an integer T, where T is the number of test cases.

The first line of each test case contains an integer n (1 ≤ n ≤ 2 × 105), where n is the number of the boxes in the game.

The second line of each test case contains n integers c1, c2, …, cn (1 ≤ ci ≤ 2 × 105), where ci is the color of the ith box.

Output
For each test case, print a single line containing the minimum number of required jumps to reach the nth box starting from the 1st box.

Example
Input

3
6
9 2 4 7 1 5
5
1 2 1 1 4
6
1 2 3 1 3 2
Output
5
3
2
这个题就是求到终点的最短步数,需要利用动态规划,来求到每个颜色的箱子的最短步数,相同颜色的箱子可以直接跳,所以要先把相同颜色的箱子,开个数组来标记一下,然后在碰到之前有过颜色的箱子的时候进行决策、
动态转移方程就是
s t e p [ i ] = m i n ( s t e p [ i ] , s t e p [ p r e [ i ] ] + 1 ) step[i]=min(step[i],step[pre[i]]+1) step[i]=min(step[i],step[pre[i]]+1)
这里的step数组就是到第i个点的最短步数,pre[i]就是记录和第i个箱子颜色相同的位置的。上述方程是在碰到有颜色相同的箱子的时候进行决策的。
而没碰到颜色相同的箱子的话,就是单纯的步数+1。

#include<iostream>
#include<map>

using namespace std;

const int N=200007;
int a[N],step[N],pre[N];//a来记录箱子的颜色,step记录到第i个点的最短步数 
int main()                //pre记录第i个箱子可以由前面哪个箱子跳过来 
{
	int t;
	scanf("%d",&t);
	while(t--)
	{
		map<int,int> mp;
		int n;
		scanf("%d",&n);
		for(int i=1;i<=n;i++)
		scanf("%d",&a[i]);
		for(int i=1;i<=n;i++)
		{
			pre[i]=-1; //前面没有相同的箱子就记为-1 
			if(mp[a[i]])  
			{
				pre[i]=mp[a[i]]; //记录这个箱子可以由前面哪个箱子跳过来 
			}
			mp[a[i]]=i; //记录这个颜色的箱子的位置 
		}
		step[0]=-1;
		for(int i=1;i<=n;i++)
		{
			step[i]=step[i-1]+1;//先跳过去,步数+1 
			if(pre[i]!=-1) //前面有相同的箱子 
			{
				step[i]=min(step[i],step[pre[i]]+1); //开始决策 
			}
		}
		printf("%d\n",step[n]); //输出到终点的最短步数 
	}
	return 0;
}

The Hell Boy

Since the problem set was hard, here is an easy task for you to solve.

You are given an array a consisting of n integers, and your task is to calculate the summation of the multiplication of all subsets of array a. (See the note for more clarifications)

A subset of an array a is defined as a set of elements that can be obtained by deleting zero or more elements from the original array a.

Input
The first line contains an integer T, where T is the number of test cases.

The first line of each test case contains an integer n (1 ≤ n ≤ 105), where n is the size of array a.

The second line of each test case contains n integers a1, a2, …, an (1 ≤ ai ≤ 106), giving the array a.

Output
For each test case, print a single line containing the summation of the multiplication of all subsets of array a. Since this number may be too large, print the answer modulo 109 + 7.

Example
Input
3
3
1 2 3
2
3 5
1
4512
Output
23
23
4512
Note
As input/output can reach huge size it is recommended to use fast input/output methods: for example, prefer to use scanf/printf instead of cin/cout in C++, prefer to use BufferedReader/PrintWriter instead of Scanner/System.out in Java.

In the first test case, the array a has 6 subsets, and the answer is calculated as follow:

(1) + (2) + (3) + (1 × 2) + (1 × 3) + (2 × 3) + (1 × 2 × 3) = 23
这个题偏向于数学题,找规律,推导数学公式以及递推的思想
i=1时,即A,则等于A
i=2时,即A,B,则等于A+B+AB=BA+A+B
i=3时,即A B C,则等于A+B+C+AB+AC+BC+ABC=C
(AB+A+B)+A+B+AB+C
所以递推关系是就是 d p [ i ] = d p [ i − 1 ] ∗ a [ i ] + a [ i ] + d p [ i − 1 ] dp[i]=dp[i-1]*a[i]+a[i]+dp[i-1] dp[i]=dp[i1]a[i]+a[i]+dp[i1]
然后直接套用公式即可

#include<iostream>
using namespace std;
const int N=1e9+7;
#define ll long long
int main()
{
	int t;
	scanf("%d",&t);
	while(t--)
	{
		int n;
		scanf("%d",&n);
		ll res=0;
		int k;
		for(int i=0;i<n;i++)
		{
			scanf("%d",&k);
			res=(res*k+res+k)%N;
			//dp[i]=dp[i-1]*a[i]+a[i]+dp[i-1]
		}
		printf("%lld\n",res%N);
	}
	return 0;
}

Palindromes Building

An anagram is a word or phrase formed by rearranging the letters of another word or phrase, using all the original letters exactly once, such as “post”, “stop”, and “spot”.

You are given a string s consisting of lowercase English letters. Your task is to count how many distinct anagrams of the string s are palindromes.

A palindrome is a word, phrase, number, or other sequence of characters which reads the same backward as forward, such as “madam” or “racecar”.

For example, “aabb” has 6 distinct anagrams, which are: “aabb”, “abab”, “abba”, “baab”, “baba”, “bbaa”. Two of the previous anagrams are palindromes, which are: “abba”, “baab”.

Input
The first line contains an integer T, where T is the number of test cases.

The first line of each test case contains an integer n (1 ≤ n ≤ 20), where n is the length of the string s.

The second line of each test contains a string s of length n consisting of lowercase English letters.

Output
For each test case, print a single line containing how many distinct anagrams of the string s are palindromes.

Example
Input

5
4
aabb
6
ababbc
6
abaaba
12
babacbcbcaca
14
aaaabbcaaaabbc
Output
2
0
3
90
105

这题简单来说就是要计算,现有的字串,能有多少种回文串的形式。
那么把问题就分解成了,两部分,一是判断现有的字符串能不能变成回文串的形式,二是能组成的情况下,能组成多少种回文串?
一、判断能不能变成回文串的形式
根据n的奇偶性判断能不能产生回文串,如果n为偶数,那么每种字母出现的次数都要为偶数才会出现回文串,如果n是奇数,那么有且必须有一种字母的出现次数为奇数,其余字母都出现偶数次。
二、会有多少种回文串?
排列组合来算回文串种类,因为是回文所以从中间为轴对称,只算前半段的情况就行了,如果是总偶数个,那么不存在正中间的数,如果是总奇数个,那么中间的字母是固定的,必须是出现次数为奇数的那种字母,所以也可以直接用n/2(正好是向下取整)的方式来算前半段。
前半段总的排列情况就是 (n/2)!,其中重复的情况就是字母相同排列互换,比如两个a,aa和aa就是重复的情况,都算在(n/2)!里了,所以要除以每一种字母的 (num[i]/2),把重复的情况除掉就是最终答案了
说实话,回文串+排列组合真滴恶心
下面展示代码

#include<iostream>
#include<cstring>
#include<algorithm>
using namespace std;
#define ll long long
int num[26];
int flag;

int main()
{
	int t;
	scanf("%d",&t);
	while(t--)
	{
		memset(num,0,sizeof(num));
		int n;
		scanf("%d",&n);
		string s;
		cin>>s;
	    flag=1;
	    for(int i=0;i<n;i++)
	    {
	    	num[s[i]-'a']++; //这里记录出现次数 
		}
		if(n%2==0) //判断是否可以变换成回文串 
		{
			for(int i=0;i<26;i++)
			{
				if(num[i]%2!=0)
				{
					flag=0;  
					break;
				}
			}
		}
		else 
		{
			int x=0;
			for(int i=0;i<26;i++)
			{
				if(num[i]%2==1)
				x++;
			}
			if(x!=1) flag=0;
		}
		if(flag==0)
		{
			printf("0\n");
			continue;
		}
		//求回文串的种类数,利用阶乘式 
		ll res1=1,res2=1;
		for(int i=1;i<=(n/2);i++)
		{
			res1=res1*i;
		}
		//下面这是求由于重复的字母所产生的种类数 
		for(int i=0;i<26;i++)
		{
			if(num[i])
			{
				for(int j=1;j<=(num[i]/2);j++)
				{
					res2=res2*j;
				}
			 } 
		}
		//相除就是把重复情况都删去, 
		printf("%lld\n",res1/res2);
	}
	return 0;
}

To be continued

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值