[jzoj]2018.07.15【NOIP普及组】模拟赛D组:解题报告

1.蚂蚁

题目:

 

n只蚂蚁以每秒1cm的速度在长为Lcm的竿子上爬行。当蚂蚁爬到竿子的端点时就会掉落。由于竿子太细,两只蚂蚁相遇时,它们不能交错通过,只能各自反向爬回去。对于每只蚂蚁,我们知道它距离竿子左端的距离xi,但不知道它当前的朝向。请计算各种情况当中,所有蚂蚁落下竿子所需的最短时间和最长时间。
例如:竿子长10cm,3只蚂蚁位置为2 6 7,最短需要4秒(左、右、右),最长需要8秒(右、右、右)。

 

输入:

第1行:2个整数N和L,N为蚂蚁的数量,L为杆子的长度(1 <= L <= 10^9, 1 <= N <= 50000)

 

第2 - N + 1行:每行一个整数A[i],表示蚂蚁的位置(0 < A[i] < L)

 

输出:

 

输出2个数,中间用空格分隔,分别表示最短时间和最长时间。

 

数据范围:

 

对于10%的数据n≤1
对于20%的数据n≤2
对于50%的数据n≤5
对于60%的数据n≤50
对于70%的数据n≤500
对于80%的数据n≤5000

 

对于100%的数据n≤50000

思路:

Code:

#include<algorithm>
#include<iostream>
#include<cstring>
#include<cstdio>
using namespace std;
#define MAXN 50005
int a[MAXN];
int main()
{
    freopen("t1.in","r",stdin);
    freopen("t1.out","w",stdout);
    int n,l;
    cin >> n >> l;
    int x = 0,y = 0,k;
    for (int i = 1;i <= n;i++)
    {
        cin >> a[i];
        k = min(a[i],l - a[i]);
		if (x < k) 
			x = k;
        k = max(a[i],l - a[i]);
		if (y < k) 
			y = k;
    }
    cout << x << " " << y;
    fclose(stdin);
    fclose(stdout);
    return 0;
}

2.max

题目:

 

一个正整数一般可以分为几个互不相同的自然数的和,如3=1+2,4=1+3,5=1+4=2+3,6=1+5=2+4,…。
现在你的任务是将指定的正整数n分解成m个(m>=1)互不相同的自然数的和,且使这些自然数的乘积最大。

 

输入:

 

只一个正整数n,(3≤n≤10000)。

 

输出:

 

第一行是分解方案,相邻的数之间用一个空格分开,并且按由小到大的顺序。
第二行是最大的乘积。

 

数据范围:

30%的数据  3<=n<=100

思路:

这道题可以用dp做,也可以找出规律,那么就是一个数如果尽量的拆分成从2+3+4+5......这样的格式,
可以推出n可以从2加到几,这里我是解了一个二次方程,设从n = 2加到k,那么(2+k)*(k-1)/2=n,那么把k解出来就行了,
然后有可能从2加到k不是刚好等于n,那么剩下的,就及有先后面的加数的方法平均分配。

Code:

# include<cmath>
# include<cstdio>    
# include<cstring>
# include<iostream>
# include <algorithm>
using namespace std;
int n = 0,tot = 0,sum[10003];
int ans[251],che[251];
int main()
{
	freopen("max.in","r",stdin);
	freopen("max.out","w",stdout);
    scanf("%d",&n);
    double gs = (double)n * 2 + 2;
	gs = (double)1 + 4 * gs;
	gs = sqrt(gs);
	gs = (-1 + gs) / 2;
    int tot = (int)gs;
    int fen = n - ((tot + 2) * (tot - 1) / 2);
    int jia = fen / (tot - 1),sy = fen - (tot - 1) * jia;
    for(int x = 2;x <= tot;x++)
		sum[x] = x + jia;
    for(int x = tot;x > tot - sy;x--)
		sum[x]++;
    ans[0] = 1,ans[1] = 1;
    for(int x = 2;x <= tot;x++)
	{
        cout << sum[x] << " ";
        memset(che,0,sizeof(che));
        for(int y = 1;y <= ans[0];y++)
			che[y] = ans[y] * sum[x];
        int cnt = 1;
        while(1)
		{
            if(cnt > ans[0] && che[cnt] == 0)
				break;
            ans[cnt] = che[cnt] % 10;
            che[cnt + 1] += che[cnt] / 10;
            cnt++;
        }
        ans[0] = cnt - 1;
    }
	cout << endl;
    for(int x = ans[0];x >= 1;x--)
		cout << ans[x];
}

3.取物游戏

 

题目:

 

一天,还是Famer KXP,他在与他的好朋友Famer John玩一个游戏,规则如下:KXP站在起点,桌子上放置着一排物品,每个物品都有它的质量和距离起点的位置。KXP的基础分=最远物品距离*2+拿的物品的质量和,最终分数=最远物品距离*2+拿的物品的质量和*Y(Y为该物品是第Y个被拿的)KXP想提前知道自己在拿X个物品时基础分最高时最终分数为多少,若在拿K个时有多种情况能达到最高基础分则只输出X小于K时的情况(即发现有多种情况就立即结束程序),希望你帮忙解决。(K不一定被询问)

 

输入:

 

第一行两个数N,M,接下来N行,每行一个a,b,为物品的质量及距离,再接下来M行,为M个询问,每行一个X,为求拿X个时最大分数。(a,b无重复,询问从小到大)

 

输出:

 

输出M行,每行一个数,为KXP的最终分数。

 

数据范围:

 

30% 0<M<N<10
50% 0<M<N<2500
100% 0<M<3000<N<6000
100% 答案小于2^61  0<a,b<10^5 询问数严格小于N
(数据随机,高兴点)

 

提示:

 

<说在这里,题目坑怪KXP贪玩,不怪出题人>
取一个物品 2号 基础2 + 3 * 2 = 8 最终 3 * 2 + 2 * 1 = 8
取两个物品 2号,3号 基础3 * 2 + 2 + 3 = 11 最终 3 * 2 + 3 * 2 + 2 * 1 = 14

 

取三个物品 1号,2号,3号  基础 3*2+3+2+1=12 最终 3 * 2 + 3 * 3 + 2 * 2 + 1 * 1 = 20

思路:

30%可以暴力搜索;
50%可以贪心算法+快速排序;(贪心选物,排序确定顺序)
100%可以先用贪心法找出该取的物品,然后用堆来维护取出顺序。

原理:第X+1次取物肯定实在第X次的基础上,从剩余物品中找出距离更大或质量更大的物品,堆可以以logN的速度插入,到需要计算最终分时再逐个取出,不需要时只是维护便好。(最终得分来源于基础分,即将已选的物品质量从小到大排个序,先取小后取大)(要多暴力就有多暴力,复杂度N*N *logN*1/4以下)当然,可以不暴力,有更优的维护方法,可以自行思考。

Code:

对不起,我不会

3.围攻

 

题目:

 

经过刘邦的严密缉查,项羽的位置也就水落石出了。刘邦便趁机集合军队,进行对项羽的围攻。为了增加胜率,张良研究出一种全新的战法,目的就是一举打败难缠的项羽。
这种军队共有N个单位,一个接着一个排成一排,每个单位可以是士兵,或者是战车,这样的组合可以爆发出意想不到的强大战斗力;但有一点,两辆战车不能相邻,否则会发生剐蹭等不好的事故。刘邦希望知道这N个单位的军队都多少种不同的排列方法,以便在战场中随机应变。两种军队的排列方法是不同的,当且仅当某一个单位对应不同,如:第i位这种是士兵,那种是战车……

 

输入:

 

输入仅一行,一个整数N。

 

输出:

 

输出仅一行,一个整数,表示排列的方案数。
答案对 10 ^ 8 + 7 取模

 

数据范围:

 

对于30%的数据:N≤15;
对于70%的数据:N≤10^6;

 

对于100%的数据:N≤10^18。

思路:

这是一道数学题,可以用DP推导。设f[i][0]表示进行到第i位,且第i位放士兵的总方案数;再设f[i][1]表示表示进行到第i位,且第i位放战车的总方案数。那么根据题意,得转移式如下:
                                               f[i][0]=f[i-1][0]+f[i-1][1] 
                                               f[i][1]=f[i-1][0] 
设第i位总方案数为g[i],所以整理得:
                                                       g[i]=f[i][0]+f[i][1]
                                                       =f[i-1][0]*2+f[i-1][1]
                                                       =g[i-1]+f[i-2][0]+f[i-2][1]
                                                       =g[i-1]+g[i-2]
所以长度为i的方案数就等于长度为i - 1的方案数 + 长度为i - 2的方案数
于是推导就相当于斐波拉契数列!

可是O(N)也只能过70%的数据点,100%则矩阵乘法 + 快速幂,O(logN)AC。

Code:

#include<iostream>
#include<cstdio>
using namespace std;
struct matrix
{
	long long a[15][15];
};
matrix chu,die;
matrix mul(matrix a1,matrix a2,int ii)
{
	int i,j,k;
	matrix c;
	for(i = 1;i <= ii;i++)
	 	for(j = 1;j <= ii;j++)
	 	{
  	       	c.a[i][j] = 0;
		  	for(k = 1;k <= ii;k++)
		    	c.a[i][j] += a1.a[i][k] * a2.a[k][j] % 100000007;
		  	c.a[i][j] %= 100000007;	
  	  	}
   return c;
}
void qm(long long nn)
{
	while(nn > 0)
	{
		if(nn % 2 == 1) 
			chu = mul(chu,die,2);
		die = mul(die,die,2);
		nn = nn >> 1;
		chu.a[2][1] = 0; 
			chu.a[2][2] = 0;
	}
}
int main()
{
	long long n;
	cin >> n;
	n += 2;
	chu.a[1][1] = 1; chu.a[1][2] = 1;
	die.a[1][1] = 1; die.a[1][2] = 1;
	die.a[2][1] = 1; die.a[2][2] = 0;
	if(n != 1 || n != 2)
		qm(n - 2);
	cout << chu.a[1][1];
	return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值