20181005提高测试
Posted lstoi
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了20181005提高测试相关的知识,希望对你有一定的参考价值。
T1: 阶乘
fact.cpp
【问题描述】
有 n个正整数 a[i],设它们乘积为 p,你可以给 p乘上一个正整数 q,使 p*q刚好为正
整数m的阶乘,求m的最小值。
【输入】
共两行。
第一行一个正整数n。
第二行n个正整数a[i]。
【输出】
共一行
一个正整数m。
【输入样例】
1
6
【输出样例】
3
样例解释:
当p=6,q=1时,p*q=3!
【数据范围与约定】
对于10%的数据,n<=10
对于30%的数据,n<=1000
对于100%的数据,n<=100000,a[i]<=100000
【错解】
首先猜想和最大值有关,然而样例都凑不出来
然后开始瞎搞,从大往小拆成两个没出现过的数的乘积
过了样例……
等等?好像最大有重复的?
然后真瞎搞
WA1+TLE9
期间想到二分,但并不知道怎么check,索性放弃
【正解】
二分
由于是阶乘,x!某个因数a出现次数为
(sum _{i=1}^{infty} [frac{x}{a^{i}}])
把读入的数分解,然后就可以check辣
#include <iostream>
#include <cstdio>
#include <cctype>
#define MAXN 100005
#define MAX 100000
#define int long long
using namespace std;
int np[MAXN];
int p[MAXN],t[MAXN],tot,pr[MAXN];
inline int read()
{
int ans=0,f=1;
char c=getchar();
while (!isdigit(c))
{
if (c=='-')
f=-1;
c=getchar();
}
while (isdigit(c))
ans=(ans<<3)+(ans<<1)+(c^48),c=getchar();
return f*ans;
}
void write(int x)
{
if (x<0)
putchar('-'),x=-x;
if (x<10)
{
putchar(x^48);
return;
}
write(x/10);
putchar((x%10)^48);
}
void getprime()
{
np[1]=1;
for (int i=2;i<=MAX;i++)
{
if (!np[i])
{
p[++tot]=i;
pr[i]=tot;
}
for (int j=1;j<=tot&&i*p[j]<=MAX;j++)
{
np[i*p[j]]=1;
if (i%p[j]==0)
break;
}
}
}
void depart(int x)
{
if (pr[x])
{
t[pr[x]]++;
return;
}
int tx=x;
for (int i=2;i<=tx;i++)
while (tx%i==0)
{
tx/=i;
t[pr[i]]++;
}
}
bool check(int x)
{
for (int i=1;i<=tot;i++)
{
int tx=x;
int sum=0;
while (tx)
{
tx/=p[i];
sum+=tx;
}
if (sum<t[i])
return false;
}
return true;
}
main()
{
freopen("fact.in","r",stdin);
freopen("fact.out","w",stdout);
getprime();
int n=read();
for (int i=1;i<=n;i++)
depart(read());
int l=0,r=1e8;
while (l<r)
{
int mid=(l+r)>>1;
if (check(mid))
r=mid;
else
l=mid+1;
}
write(l);
return 0;
}
T2:上升序列
(lis)
【问题描述】
给出一个长度为 m 的上升序列 A(1 ≤ A[i]≤ n), 请你求出有多少种 1...n 的排列, 满足
A 是它的一个 LIS.
【输入】
第一行两个整数 n,m.
接下来一行 m 个整数, 表示 A.
【输出】
一行一个整数表示答案.
【输入样例1】
5 3
1 3 4
【输出样例1】
11
【输入样例2】
4 2
3 4
【输出样例2】
5
【数据范围与约定】
对于前 30% 的数据, n ≤ 9;
对于前 60% 的数据, n ≤ 12;
对于 100% 的数据, 1 ≤ m ≤ n ≤ 15.
【错解】
一看数据范围就知道是暴搜
发现没有规律,怒写了个nlogn暴力check
用链表维护
没有减掉枝
AC6+TLE4
【正解】
为什么不能状压呢
首先回忆nlogn求法:
定义d[i]为结尾最小的LIS,然后二分插入来转移
即nlogn求时最后的那个数组
当然不是这么做的
设状态s表示选了那些数,s1表示d中哪些数出现了
举个生动形象的栗子:2 3 1 4……
s={1,1,1,1,0}
d={1,4}
s1={1,0,0,1,0}
因为d是单调增的,所以我们可以用s1来还原d
即如果s1[i]为1,那么i在d中出现的位置为1~i的1的个数
然后可以(f(s,s1))定义状态
然后枚举插入一个数,就可以转移了
具体操作:插入i时,前面有k位
s|=i
s1前k位转成d,再把后面接上
如在3、1之间插入5
s={1,1,1,1,1}
s1不变
边界:当s为全集时,(f(s,s1)=(d长度为原序列长度))
然而复杂度不可接受
观察发现s1为s子集
所以可以三进制表示:0表示都不在,1表示在s不在s1,2表示既在s又在s1
然后可以AC此题
[代码待补]
相遇
(meet)
【问题描述】
豪哥生活在一个 n 个点的树形城市里面,每一天都要走来走去。虽然走的是比较的
多,但是豪哥在这个城市里面的朋友并不是很多。
当某一天,猴哥给他展现了一下大佬风范之后,豪哥决定要获得一些交往机会来提升交
往能力。豪哥现在已经物色上了一条友,打算和它(豪哥并不让吃瓜群众知道性别)交
往。豪哥现在 spy 了一下这个人的所有行程起点和终点,豪哥打算从终点开始走到起点与
其相遇。但是豪哥是想找话题的,他想知道以前有多少次行程和此次行程是有交集的,这
样豪哥就可以搭上话了。这个路径与之前路径的有交集数量作为豪哥此次的交往机会。
但是豪哥急着要做交往准备,所以算什么交往机会的小事情就交给你了。
【输入】
第一行一个正整数 n表示节点个数。接下来 n-1行,每行两个正整数分别是 u,v表示节点
u和 v之间有连边。接下来一行一个 正整数 m表示路径个数。然后有 m行,每行两个正整
数分别是u,v分别表示 u到v之间有一条路径。
【输出】
输出共m行,每行一个整数,第 i行表示豪哥在这条路径上获得的交往机会。
【输入样例】
5
1 2
1 3
3 4
3 5
4
4 5
4 2
1 3
1 2
【输出样例】
0
1
2
2
【数据范围与约定】
对于20%的数据n,m≤2000
对于另外20%的数据n,m≤50000
对于另外10%的数据n,m≤200000保证树形结构是一条链
对于另外50%的数据n,m≤200000
【错解】
woc提高考树剖?还没法维护?
骗分,放弃
WA10
【正解】
一个结论:树上两条路径有交集,当且仅当其中一条路径的LCA在另一条路径上
然后就转化为:
单点修改,路径查值
路径修改,单点查值
然后直接上树链……什么?被卡了?
考虑路径可以转换为4个点到根路径,做个类似于树上前缀和的玩意
然后直接上树剖
因为是单点查值,到根路径对其有影响当且仅当在其子树上
然后成了子树 树状数组维护dfs序即可
[代码待补]
以上是关于20181005提高测试的主要内容,如果未能解决你的问题,请参考以下文章