目录
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)将要放进小根堆时,发现大根堆的堆顶元素大于当前要放进小根堆的元素。
(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;