河南理工大学2018暑假集训第三次积分赛 2018/08/05

A Math Problem

时间限制   1s        内存限制     128Mb


求:
 ps:[x]表示x向下取整
输入
第一行一个整数T表示测试组数。(0<=T<=10)
第二行一个n和k,n表示序列a的长度。(1<=n,k<=1e6)
第三行n个整数表示ai (0<=ai<=1e6)
输出
每组数据输出题目描述的求和值。


输入样例
2
2 1
3 5
3 2
1 2 3
输出样例
8
2
2

思路:签到题,我们直接暴力求解就行了。

代码如下:

#include
#include
#include
int main()
{
	int t,n,i;
	long long sum,a,k;
	scanf("%d",&t);
	while(t--)
	{
		sum=0;
		scanf("%d %d",&n,&k);
		for(i=0;i

 

C Multivariate function

时间限制   1s        内存限制     512Mb


输入
第一行一个整数T,表示T组测试数据(1 T 10).
每组数据第一行一个整数n (4<=n<=1000).
第二行n个浮点数: x1; x2:::xn; (1<= xi <= 1000000).
输出
输出最大值y,保留三位小数.
输入样例
2
4
1.0 2.0 3.0 4.0
5
1.6 2.6 7.1 2.3 2.6
输出样例
0.167
1.530
样例解释

2

思路:由于过于复杂,放弃了。

标准代码:

#include 
using namespace std;

const int MAXN = 1e3 + 10;
double a[MAXN], b[MAXN];

int main() {
	int T, n;
	scanf("%d", &T);
	while(T--) {
		scanf("%d", &n);
		for(int i = 1; i <= n; ++i) scanf("%lf", &a[i]);
		int p = 0;
		for(int i = 3; i <= n - 1; ++i) {
			double ans = 0.0;
			for(int j = i + 1; j <= n; ++j) {
				ans = max(ans, a[i] / a[j]);
			}
			b[i] = ans;
		}
		for(int i = n - 2; i >= 3; --i) {
			b[i] = max(b[i], b[i + 1]);
		}
		double y = -1e20;
		for(int i = 1; i <= n - 3; ++i) {
			for(int j = i + 1; j <= n - 2; ++j) {
				y = max(y, (a[j] * b[j + 1] - a[i]) / (a[i] + a[j]));
			}
		}
		printf("%.3lf\n", y);
	}
	return 0;
}

D Longest Increasing Subsequence

时间限制   1s        内存限制     512Mb


给出一组长度为n的序列,a1; a2; a3; a4; ; ; an, 求出这个序列长度为k的严格递增子序列的个数


输入
第一行输入T组数据T(0<=T <= 10)
第二行输入序列大小n(1 <= n <= 100),长度k(1 <= k <= n)
第三行输入n个数字ai(0 <= ai <= 1e9)


输出
数据规模很大, 答案请对1e9 + 7取模


输入样例
2
3 2
1 2 2
3 2
1 2 3


输出样例
2
3

思路:这个题用动态规划(dp)可以求解

标准代码:

#include 
using namespace std;

const int MAXN = (int)5e2+10;
const int mod = (int)1e9+7;

int dp[110][110];
int arr[110];
int main() {
    int n, k, T;
    cin >> T;
    while(T--) {
        cin >> n >> k;
        memset(dp, 0, sizeof dp);
        for(int i = 1; i <= n; ++i) {
            cin >> arr[i];
            dp[i][1] = 1;
        }
        for(int i = 1; i <= n; ++i) {
            for(int j = 2; j <= i; ++j) {
                for(int z = 1; z < i; ++z) {
                    if(arr[z] < arr[i])
                        dp[i][j] = (dp[i][j]+dp[z][j-1]) % mod;
                }
            }
        }
        int ans = 0;
        for(int i = 1; i <= n; ++i) {
            ans = (ans + dp[i][k]) % mod;
        }
        cout << ans << endl;
    }
    return 0;
}

E 玩游戏

时间限制   1s        内存限制     512Mb


zxy和wfy用几堆石子在做游戏。偶数堆石子排成一行,每堆都有正整数颗石子a[i] 。游戏以谁手中的石子最多来决出胜负。石子的总数是奇数,所以没有平局。
zxy和wfy轮流进行,wfy先开始。每回合,玩家从一行的开始或结束处取走整堆石头。这种情况一直持续到没有更多的石子堆为止,此时手中石子最多的玩家获胜。
假设zxy和wfy都发挥出最佳水平,当zxy赢得比赛时输出“zxy”,当wfy赢得比赛时输出“wfy”。


输入
有T组输入,每个输入后有一个n,在n后有n个数字,a1; a2::::an。
n的范围是1 <= n <= 1e5
1 <= a <= 1e5


输出
输出胜利者的名字"zxy"或者"wfy"


输入样例
1
4
5 3 4 5


输出样例
wfy


提示
wfy先开始,只能拿前5 颗或后5 颗石子。
假设他取了前5 颗,这一行就变成了[3,4,5] 。
如果zxy拿走前3 颗,那么剩下的是[4,5],wfy拿走后5 颗赢得10 分。
如果zxy拿走后5 颗,那么剩下的是[3,4],wfy拿走后4 颗赢得9 分。
这表明,取前5 颗石子对wfy来说是一个胜利的举动,所以我们输出wfy 。

思路:这个题目如果做过这种题目的话,应该大概可以推测出来赢的永远是wfy,结果也确实是wfy一直赢,但是我们怎么证明呢?用dp。我们对每一次状态进行结果的推测,如果某种状态能够保证不管zxy如何选,总是wfy赢,那么我们就可以确定wfy一定赢,否则就是zxy赢。

代码如下:

//简易做法 
#include
#include
#include
#include
using namespace std;
int main()
{
	int t,n,a;
	scanf("%d",&t);
	while(t--)
	{
		scanf("%d",&n);
		for(int i=0;i

F 辞树的肥宅快乐水

时间限制   1s        内存限制     512Mb
题目描述
又到了基情四射的夏天,大家出去约妹子,而肥宅辞树只想宅在机房喝肥宅快乐水。辞树一下子买了n瓶肥宅快乐水。已知他一天里至少喝掉一瓶肥宅水且他是一口干掉一整瓶。(肥宅Orz)
他想要知道自己一共有多少种喝肥宅水的方案。两种方案被认为是不同的,当且仅当辞树买的这些肥宅水能喝的天数不同,或者存在一天两种方案喝的肥宅水瓶数不同。
输入
第一行输入一个正整数T,代表有T组数据(0 < T < 11) 每组数据有一个正整数n,代表辞树买了n瓶肥宅快乐水。(0 < n < 108)
输出
对于每组数据,输出一行,将方案数用二进制表示输出。
输入样例
1
3
输出样例
100
提示
3瓶肥宅快乐水的分配方式如下
1 1 1(三天喝完,一天一瓶)
2 1(两天喝完,第一天两瓶,第二天一瓶)
1 2(两天喝完,第一天一瓶,第二天两瓶)

思路:这个题目如果试几组数据的话就会发现它的结果总是2的次方,结果总是2^(n-1),因此我们只要输入输出就行了,由于这个题目数据非常大,用C/C++是没有办法储存这么大的数字的,所以我们直接考这个捷径就可以了。

代码如下:

#include
#include
#include
#include
using namespace std;
int main()
{
	int t;
	long long x;
	scanf("%d",&t);
	while(t--)
	{
		scanf("%lld",&x);
		printf("1");
		for(int i=1;i

G From The New WorldⅠ

时间限制   1s        内存限制     512Mb


千年之后,「新世界」的孩子们被彻底地控制和管束著,不合适的记忆被消去,被认为有问题的孩子如同不良产品般被处理......
Saki和她的小伙伴们在完人学校迎来了第一个学期考试,考试规则如下:要求你使用自己的咒力对规定的图画进行有限次的念写,你需要找到其中的咒术节点并尽可能的削弱它的咒力值,最后输出最长的咒术节点的长度.


请你用程序的方式来完成考试,可把图画抽象为一段具体的序列,咒术节点可看为特定的数值,特定的数值连续出现越多咒术节点的咒力值越大.每次操作只能将要修改的值修改为自己的咒力值.(即:给定长度为n的序列,用给出的自身咒力值p替换序列中k个数,使规定的数值x连续出现的最长长度最短,输出最终最长的x的连续的长度.保证p! = x)


输入
多组输入,请处理到文件结束
第一行依次输入n; p; x; k. n表示序列的长度,p表示自身咒力值,x表示咒术节点的值,k表示规定修改次数.
(1 <= n <= 100000; 1 <= p <= 20; 0 <= x <= 100000; 0 <= k <= n)
第二行输入n个ai (0 <= ai <= 100000000)表示序列n个元素的值


输出
每行输出咒术节点连续出现最大长度.


输入样例
7 3 5 3
8 2 5 5 5 5 5


输出样例
1


提示
样例解释:修改后的序列为8 2 5 3 3 3 5 最大长度为1

 

思路:(个人理解,仅供参考!!!)

题目很长,但题意很清晰,我们需要把所有的指定序列缩减的尽可能小。

样例的最长指定序列为5 5 5 5 5 ,长度为5,我们可以修改三次。

第一次,我们修改最中间的,那么他就变成了两个5 5,此时长度就变成了2。

第二次和第三次我们分别把两个5 5变成一个5,此时我们得到了最短的指定序列,因此答案为1.

对这个题目来讲,我们只需要统计所有指定序列的所有长度,然后把最长的分成较短的两节,依次进行k次,得到的最终结果即为答案。

标准代码:

#include 
#include 
#include 
#include 
#include 
using namespace std;
typedef long long LL;
const int INF = 0x3f3f3f3f;
const int MAXN = 1E5+11;
int a[MAXN];
int b[MAXN];
int n,p,x,k,tl;
bool check(int tk){
	int cnt = 0;//处理到tk长需要的替换次数
	for (int i = 0; i < tl; ++i){
		if (b[i] > tk){
			cnt += (b[i] - tk)/(tk + 1);
		}
	}
	if (cnt <= k) return true;
	return false;
}
int main(){
	while (~scanf("%d %d %d %d",&n,&p,&x,&k)){
		memset(a,0,sizeof(a));
		int cx = 0;
		for (int i = 0; i < n; ++i){
			scanf("%d",&a[i]);
			if (a[i] == x) cx++;
		}
		if (cx <= k) puts("0");
		else{
			int tml = 0;int j;
			tl = 0;
			for (int i = 0; i < n; ){
				if (a[i] == x){
					j = i; tml = 0;
					while (j < n && a[j++] == a[i]){
						tml++;
					}
					b[tl++] = tml; tml = 0; i = j;
				}else ++i;
			}
			int l = 0,r = n;
			while (l <= r){
				int mid = (l + r)>>1;
				if (check(mid)) r = mid - 1;
				else l = mid + 1;
			}
			printf("%d\n",r+1);
		}
	}
    return 0;
}

H 真。签到题

时间限制   1s        内存限制     512Mb
A国的n个作战通信基站大部分被C国的特种部队破坏,基站编号1到n,只剩下编号为a和b的通信基站完好,为了快速恢复通信,A国派出zzx和fk两位工程师去修复,A国的通信基站有一个特殊隐蔽的备用系统,只有让两个完好的通信基站向编号为x的基站发送信号,x是这两个完好基站编号的和或者差的绝对值,该被破坏的基站的备用系统才会被激活,工程师才能恢复被破坏的基站,假设zzx工程师先到达可修复的基站进行修复,接着fk去修复下一个,两人轮流修复,问谁会修复最后一个可修复的基站


输入
输入n,a,b三个整数,代表基站的总数n和剩下的完好的两个基站的编号a,b;
1 < n <= 1e5
1 <= a; b <= n
处理到文件结束


输出
若zzx修复最后一个,输出zzx,否则输出fk


输入样例


5 1 4
10 3 7


输出样例


zzx
fk
 

思路:这个题思路有点难想,我们首先需要知道能修复的信号基站有多少个。

样例5 1 4中,我们可以修复所有的基站,样例 10 3 7中,我们也可以修复所有的基站。

但是对于10 4 6 这个数据,我们只能修复2 4 6 8 10 这些基站。

从数据上可以按出,能修复的基站个数为n/gcd(a,b),由于总是zzx先修复第一个基站,所以我们只需要判断n/gcd(a,b)的奇偶性就可以得出结果。

代码如下:

#include
#include
#include
#include
using namespace std;
int gcd(int a,int b)
{
	return b?gcd(b,a%b):a;
}
int main()
{
	int n,a,b;
	while(~scanf("%d %d %d",&n,&a,&b))
	{
		int x=gcd(a,b);
		if((n/gcd(a,b))%2)
			printf("zzx\n");
		else
			printf("fk\n");
	}
	return 0;
}

I 阶乘之和

时间限制   1s        内存限制     512Mb
对于整数p,给出以下定义
p = x1! + x2! + x3! + ::: + xq!(xi < xjfor all i < j)且xi 6= 0
(注释:p等于多个数的阶乘和,并且x1; x2; x3; :::; xq为不相等的任意正整数,即组成p的阶乘不重复使用)
给定两个整数x,y,判断二者是否能满足以上定义。若二者都满足定义,设x由k1个数的阶乘和组成,y由k2个数的阶乘和组成,若k1 = k2,按下述输出格式输出二者的定义形式(输出时,阶乘按递增形式输出,例如:7=1!+3!)。


输入
第一行输入一个整数T,代表T组测试数据。(1 T 10000)
接下来T行,每行包含两个整数x,y。(1 x; y 1018)


输出
对于每组x,y输出包含两部分:
①如果二者都满足以上定义,输出“SEYES”;如果只有其一满足以上定义,输出“YNEOS”;
如果二者都不满足以上定义,则输出“ONO”。
②当x,y都满足以上定义且k1 = k2时,输出二者的定义形式。否者输出“dWvWb”。


输入样例
4
7 7
1 2
4 2
4 4


输出样例
Case 1:SEYES
7=1!+3!
7=1!+3!
Case 2:SEYES
1=1!
2=2!
Case 3:YNEOS
dWvWb
Case 4:ONO
dWvWb

思路:这个题目如果暴力求解的话是肯定求不出来的,如果我们直接对输入的数据进行拆分,所需要的时间会非常多,而且测试数据也是非常的庞大。因此我们需要找到更好的办法。

分析得知,x,y的值都在1e18的范围内,而18!=6402373705728000(16位),19!=121645100408832000(18位),因此我们只需要对2^18=262144种情况内的所有选择情况进行一次计算,储存结果,等到输入时直接判断结果即可。

代码如下:(代码有些繁琐)

#include
#include
#include
#include
#include
using namespace std;
mapp,q,num;//三个数组,分别用来 1.标记是否经过匹配存在这个数值 2.储存这个数值的组成信息 3.储存组成数值的数字的个数 
long long pow(long long i)
{
	long long sum=1;
	for(int j=1;j<=i;j++)
		sum*=j;
	return sum;
	
}
int main()
{
	long long t,i,x,y,j;
	long long a[30]={0};
	for(i=1;i<=18;i++)//预处理前十八个数字的阶乘 
		a[i]=pow(i);
	for(i=1;i<= 1<<18 ;i++){//进行2^18次查询 
		long long sum=0,x=0;
		for(j=0;j<=18;j++){//对每个i进行位运算,表示是否选择这个数字的阶乘 1为选择,0为不选择 
			if((i>>j)&1){
				sum+=a[j];
				x++;
			}
			if(sum>1e18){//约束条件,x,y小于1e18 
				p[sum]=1;//标记这个数值存在 
				q[sum]=i;//储存这个数字的阶乘组成 
				num[sum]=x;//记录构成这个数值的数字个数 
				break;
			}
			if((i>>j)<=0){//如果等于零了,就没有必要进行接下来的位运算 
				p[sum]=1;
				q[sum]=i;
				num[sum]=x;
				break;
			}
		}
	}
	scanf("%lld",&t);
	for(int k=1;k<=t;k++){
		scanf("%lld %lld",&x,&y);
		if(p[x]==1 && p[y]==1){//判断两个数值是否存在 
			printf("Case %d:SEYES\n",k);
			if(num[x]==num[y]){//判断两个的阶乘个数是否一样 
				printf("%lld=",x);
				int ok=0;
				for(int j=1;j<=18;j++){
					if(q[x]>>j & 1)
					{
						if(ok==0)
						{
							printf("%lld!",j);
							ok=1;
						}
						else
							printf("+%lld!",j);
					}
				}
				printf("\n%lld=",y);
				ok=0;
				for(int j=1;j<=18;j++){
					if(q[y]>>j & 1)
					{
						if(ok==0)
						{
							printf("%lld!",j);
							ok=1;
						}
						else
							printf("+%lld!",j);
					}
				}
				printf("\n");
			}
			else
				printf("dWvWb\n");
		}
		else if(p[x]==1||p[y]==1)
			printf("Case %d:YNEOS\ndWvWb\n",k);
		else
			printf("Case %d:ONO\ndWvWb\n",k);
	}
	return 0;
}

J 过河问题

时间限制   1s        内存限制     65535KB
在漆黑的夜里,N位旅行者来到了一座狭窄而且没有护栏的桥边。如果不借助手电筒的话,大家是无论如何也不敢过桥去的。不幸的是,N个人一共只带了一只手电筒,而桥窄得只够让两个人同时过。如果各自单独过桥的话,N人所需要的时间已知;而如果两人同时过桥,所需要的时间就是走得比较慢的那个人单独行动时所需的时间。问题是,如何设计一个方案,让这N人尽快过桥。


输入
多组数据。
每组测试数据的第一行是一个整数N(从1到1000,包括1和1000)表示共有N个人要过河
每组测试数据的第二行是N个整数Si,表示此人过河所需要花时间。(Si 小于等于100,大于等于1)


输出
输出所有人都过河需要用的最少时间。


输入样例
4
1 2 5 10


输出样例
17
思路:这个题目是一个非常难的题目,样例很多人短时间内根本看不出来是怎么得到的。

对于样例,过程是这样的:

河南理工大学2018暑假集训第三次积分赛 2018/08/05_第1张图片

因此,最终结果为2+2+10+1+2=17。

指根据这个我们是做不出来这个题的,但是我们可以从这里面找到一些规律:

对于四个数a,b,x,y(a<=b<=x<=y),我们选择常规做法的话,结果为b+a+x+a+y = 2a+b+x+y;选择上面做法的话结果为b+b+y+a+b=a+3b+y;

两种方案的差值为2a+b+x+y-a-3b-y=a+x-2b;

如果a+x-2b>0即a+x>2b,那么我们就可以选择样例这样的过程,如果不符合,那么我们就用常规做法。

如果最后剩下一个没有过河,那么只有一种方案过河,即b+a+x,因此我们在最后需要判断一下人数的奇偶。

至此,这个题目就解决完了。

代码如下:

#include
#include
#include
#include
using namespace std;
bool cmp(int a,int b){
	return a>b;
}
int main()
{
	int n,t,sum,sum1,min,a[10000],sumx;
	while(~scanf("%d",&n))
	{
		memset(a,0,sizeof(a));
		for(int i=0;i

 

你可能感兴趣的:(学校练习赛)