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

1.和谐数

给定一个长度为N的序列a,对于每一个数都可选或不选,把选出的数有序组成一个新的序列b,使b序列的“和谐数”最大。一个序列的和谐数如下定义:对于位置i,如果第奇数次选的则加上bi,偶数次选的则减去bi 
注意:新的序列b必须是从左到右依次在a序列选择的,即不能打乱顺序。

数据范围:

    对于20%的数据,1<=n<=20 

    对于50%的数据,1<=n<=1000 
    对于100%的数据,1<=n<=10000000,1<=Ai<=100

输入:输入的第一行是一个n,第二行为n个数,即序列a

输出:输出一行一个整数,即表示最大的和谐数

思路:

DP: 
第一想法当然是暴力,不过这数据告诉我们暴力的期望分应该最多只有50分,然后我们发现可以用DP求解。 
因为从左往右依次选而且可选可不选,所以设 
f[i][1]表示到第i步为选择为偶数次时和谐数最大是多少. 
f[i][2]表示到第i步为选择为奇数次时和谐数最大是多少. 
然后推出状态转移方程: 
f[i][1] = max(f[i - 1][1],f[i - 1][2] - a[i]); 
f[i][2] = max(f[i - 1][2],f[i - 1][1] + a[i]); 

#include<functional>
#include<algorithm>
#include<iostream>
#include<cstring>
#include<cctype>
#include<cstdio>
#include<cmath>
int f[2][3];
int maxn(int a,int b){return a > b ? a : b;}
inline int read()
{
    int ret = 0; 
	int w = 0; 
	char ch = 0;
    while(!isdigit(ch)) 
	{
		w |= ch == '-';
		ch = getchar();
	}
    while(isdigit(ch)) 
		ret = (ret << 3) + (ret << 1) + (ch ^ 48),ch = getchar();
    return w ? -ret : ret;
}
inline void write(int x)
{
     if(x < 0) 
	 	putchar('-'),x = -x;
     if(x > 9) 
	 	write(x / 10);
     putchar(x % 10 + '0');
}
int main()
{
	freopen("a.in","r",stdin);
	freopen("a.out","w",stdout);
	int n,x;
	n = read();
	for(int i = 1;i <= n;i++)
	{
		x = read();
		f[i % 2][1] = maxn(f[(i + 1) % 2][1],f[(i + 1) % 2][2] - x);
		f[i % 2][2] = maxn(f[(i + 1) % 2][2],f[(i + 1) % 2][1] + x);
	}
	write(maxn(f[n % 2][1],f[n % 2][2]));
	return 0;
}

2.分数

题目: 

求n1个a[i]的乘积跟n2个b[i]乘积的既约分数,(既约分数就是分子分母最大公约数为1的分数)。

对于20%的数据,n1,n2<=10,Ai,Bi<=10 
对于60%的数据,n1,n2<=1000,Ai,Bi<=1000 
对于100%的数据,n1,n2<=100000,Ai,Bi<=10000 
数据保证不会出现分数的值为0的情况

思路: 

20分的就是直接得到乘积后直接用辗转相除法约去最大公因数即可 
时间复杂度:O(n) 
60分就使用高精度,把两个序列两两去除公因数 
AC的话: 
因为Ai,Bi<=10000,所以可以先塞素数,然后对其分解质因数,把合数化成质数(质数则不变),然后2个序列a,b都这样做,最后用两个序列的分解出来的数相互抵消,类似于去重。 
然后将2个序列剩下的数分别用高精度算出乘积,然后直接输出 
PS:抵消完后的序列乘积满足既约分数。 
不过我们在高精度的时候要用到压位,不然容易超时,即改变进制,因为Ai,Bi<=10000,所以长整型最大压位到200000左右… 
时间复杂度O(n*m) 

M为常数,不会超过100,因为10000以内我们只需分解到100即可,分解完后如果还大于1,则可证这一定是个质数

#include<algorithm>
#include<iostream>
#include<cstring>
#include<cctype>
#include<cstdio>
#include<cmath>
#define maxn 100010
using namespace std;
int a[maxn],b[maxn],ans[maxn],rp[maxn];
bool p[maxn];
int kp[10010];
int la;
inline void gjd(int k)
{
	int j,x;
	x = 0;
	for(j = 1;j <= la;j++)
	{
		ans[j] = k * ans[j] + x;
		x = ans[j] / 100000;
		ans[j] %= 100000;
	}
	ans[la + 1] = x;
	la++;
	if(ans[la] == 0)
		la--;
}
string check(int p)
{
	string ret;
    if (p <= 9999) 
		ret += '0';
    if (p <= 999) 
		ret += '0';
    if (p <= 99)
		ret += '0';
    if (p <= 9)  
		ret += '0';
	return ret;
}
void doa(int k)
{
	a[kp[k]]++;
     if (k == kp[k])
	 	return ;
     doa(k / kp[k]);
}
void dob(int k)
{
	b[kp[k]]++;
     if (k == kp[k])
	 	return ;
     dob(k / kp[k]);
}
inline int read()
{
    int ret = 0; 
	int w = 0; 
	char ch = 0;
    while(!isdigit(ch)) 
	{
		w |= ch == '-';
		ch = getchar();
	}
    while(isdigit(ch)) 
		ret = (ret << 3) + (ret << 1) + (ch ^ 48),ch = getchar();
    return w ? -ret : ret;
}
int main()
{
	freopen("count.in","r",stdin);
	freopen("count.out","w",stdout);
	int n,x,m,j,k;
	n = read();
	for (int i = 1;i <= n;i++)
	{
		x = read();
		a[x]++;
	}
	m = read();
	for (int i = 1;i <= m;i++)
	{
		x = read();
		b[x]++;
	}
	p[1] = true;
	kp[1] = 1;
	for (int i = 2;i <= 10000;i++)
	{
		if (p[i] == false)
		{
			j = 1;
			while (i * j <= 10000)
			{
				p[i * j] = true;
				if (kp[i * j] == 0)
					kp[i * j] = i;
				j++;
			}
		}
	}
	for (int i = 2;i <= 10000;i++)
	{
		k = a[i];
		a[i] = 0;
		for (int j = 1;j <= k;j++)
			doa(i);
		k = b[i];
		b[i] = 0;
		for (int j = 1;j <= k;j++)
			dob(i);
	}
	for (int i = 2;i <= 10000;i++)
	{
		if (a[i] >= b[i])
		{
			a[i] = a[i] - b[i];
			b[i] = 0;
		}
		else
		{
			b[i] = b[i] - a[i];
			a[i] = 0;
		}
	}
	ans[1] = 1;
	la = 1;
	for (int i = 2;i <= 10000;i++)
		for (int j = 1;j <= a[i];j++)
			gjd(i);
	cout << ans[la];
	ans[la] = 0;
	for (int i = la - 1;i >= 1;i--)
	{
		cout << check(ans[i]);
		cout << ans[i];
		ans[i] = 0;
	}
	la = 1;
	ans[1] = 1;
	for (int i = 2;i <= 10000;i++)
		for (int j = 1;j <= b[i];j++)
			gjd(i);
	cout << " ";
	if (ans[1] == 1 && la == 1){}
	else
	{
		cout << ans[la];
		for (int i = la - 1;i >= 1;i--)
		{
			cout << check(ans[i]);
			cout << ans[i];
		}
	}
}
 
 

3.终极数

题目: 

给定一个长度为n的序列a,试求出对于序列a的每一个前缀的终极数x,使得 
最小,试求出终极数t(如若有多个终极数t,只需输出最小的那个)

思路:

其实就是选中位数~ 

维护一个大根堆和一个小根堆,并且保证 1.小根堆节点个数大于等于大根堆节点个数。 2.小根堆的堆顶元素大于大根堆的堆顶元素。 这就说明有两种特殊情况: 

(1)将要放进小根堆时,发现大根堆的堆顶元素大于当前要放进小根堆的元素。 

扫描二维码关注公众号,回复: 2115573 查看本文章

(2)将要放进大根堆时,发现小根堆的堆顶元素小于此元素。 设此元素为x。 

(1)情况:把 x 放到大根堆中,并且把大根堆的堆顶放到小根堆当中,并维护两堆 性质。 

(2)情况:把 x 放到小根堆中,并且把小根堆的堆顶放到大根堆当中,并维护两堆 性质。 

详细见代码:t3题解

4.串

题目: 

给定一个0-1串,请找到一个尽可能长的子串,其中包含的0与1的个数相等。

30%的数据 串的长度<20 100%的数据 长度不超过1000000 

保证字符串只出现0,1

思路:

当0和1的差值与上一个0和1的差值相同时,那么他们之间的字符串中0和1的数目是相同的。求出最大的就行了。举个例子:前面0,1为a, b,后面0,1为aa, bb, a - b = aa - bb,  得 a - aa = b - bb;

t4题解


猜你喜欢

转载自blog.csdn.net/qq_40155097/article/details/81005275