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
分析:
二分m
二分的上界要稍微大点,1e8,不然会漏掉答案。
验证时对p分解质因子,然后求出m!里面有多少个p的各个质因子,如果都比p多,那么m就合法。
代码:
#include <cstdio>
#include <iostream>
#include <cstring>
#define LL long long
#define N 600007
using namespace std;
LL n, zs[N], T, a[N];
LL tot = 1, ans;
bool b[N];
void Pre_work(){
for (int i = 2; i <= N / 2; i++){
if (!b[i]){
zs[++zs[0]] = i;
for (int j = 1; j <= zs[0]; j++)
if (i * zs[j] > N / 2) break;
else b[zs[j] * i] = 1;
}
else{
for (int j = 1; j <= zs[0]; j++)
if (i * zs[j] > N / 2) break;
else b[zs[j] * i] = 1;
}
}
}
LL max(LL a, LL b){
return a > b ? a : b;
}
void Cl(LL x){
for (int i = 1, p = x; p > 1; i++)
for (; p % zs[i] == 0; p /= zs[i]){
if (!b[p]){
a[p]++;
T = max(T, p);
p = 1;
break;
}
a[zs[i]]++, T = max(T, zs[i]);
}
}
bool Check(LL ain){
for (int i = 1; i <= T; i++){
int j = zs[i];
LL Jl = 0;
for (LL k = j; (k <= ain) && (Jl < a[zs[i]]); k *= j) Jl += ain / k;
if (Jl < a[zs[i]]) return 0;
}
return 1;
}
void Find(){
LL l = 1, r = 100000000;
while (l < r) {
LL mid = (l + r) / 2;
if (Check(mid)) r = mid;
else l = mid + 1;
}
printf("%lld", l);
}
int main(){
freopen("fact.in", "r", stdin);
freopen("fact.out", "w", stdout);
scanf("%lld", &n);
LL x;
Pre_work();
for (int i = 1; i <= n; i++){
scanf("%lld", &x);
if (!b[x]) {
a[x]++, T = max(T, x);
continue;
}
Cl(x);
}
Find();
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.
状压dp好题。
首先需要回忆O(nlogn)O(nlogn) 的方法,我们会维护一个单调递增的d数组。
可以设计状态f(s1,s2)表示选取的数的集合是s1,然后d数组中元素的出现情况是s2。
这样转移是很简单的。
但时空都无法承受。
于是我们考虑优化,不难发现s1是s2的子集。
因此我们三进制状压dp就行了。
代码
#include<bits/stdc++.h>
#define fo(i,a,b) for(int i=a;i<=b;++i)
#define fod(i,a,b) for(int i=a;i>=b;--i)
#define N 16
#define M 14348910
#define LL long long
using namespace std;
int n,m,cf[N],a[N],wz[N],cf2[N];
LL f[M],ans;
void dfs(int k,int lim,int v)
{
if(k>n)
{
if(lim==0) ans+=f[v];
return;
}
v+=cf[k-1];
dfs(k+1,lim,v);
v+=cf[k-1];
dfs(k+1,lim-1,v);
}
int main()
{
cin>>n>>m;
fo(i,1,m) scanf("%d",&a[i]),wz[a[i]]=i;
cf[0]=cf2[0]=1;
fo(i,1,n) cf[i]=cf[i-1]*3,cf2[i]=cf2[i-1]*2;
f[0]=1;
fo(i,0,cf2[n]-2)
{
bool pd=1,c1=1;
fo(j,1,m)
{
if((i&cf2[a[j]-1])==0) pd=0;
else if(!pd)
{
c1=0;
break;
}
}
if(c1)
{
for(int i1=i;1;i1=(i1-1)&i)
{
int vi=0;
fo(j,1,n)
{
if(i1&cf2[j-1]) vi+=cf[j-1];
if(i&cf2[j-1]) vi+=cf[j-1];
}
if(f[vi])
fo(j,1,n)
{
if(vi/cf[j-1]%3==0)
{
int v=vi+2*cf[j-1];
fo(p,j+1,n)
if(v/cf[p-1]%3==2)
{
v-=cf[p-1];
break;
}
f[v]+=f[vi];
}
}
if(i1==0) break;
}
}
}
dfs(1,m,0);
printf("%lld\n",ans);return 0;
}
T3 :相遇
(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
分析:
两条路径相交的情况就是:
1、这条路径的lca在另一条路径上。
2、某条路径穿过这条路径的lca。
我们用两个树状数组来维护,用dfs序。
对于第一种情况,我们维护这个点到根节点的路径上面有多少个lca,然后相减就能得到答案。
对于第二种情况,我们在两个节点上面打一个+1的标记,在lca上面打一个-2的标记,这样就将整条链都+1了。
代码:
#include<bits/stdc++.h>
#define lowbit(x) ((x)&-(x))
#define treeSum(t,x) (sum(t,R[x])-sum(t,L[x]-1))
using namespace std;
const int N=2e5+10;int n,m,L[N],R[N],stm;
int tot,to[N*2],next[N*2],final[N],app[N];int f[N][19],dep[N];int t1[N],t2[N],ans;
void change(int *tr,int x,int v) {for (; x<=n; x+=lowbit(x)) tr[x]+=v;}
int sum(int *tr,int x)
{
int ret=0;
for (; x; x-=lowbit(x)) ret+=tr[x];
return ret;
}
void link(int x,int y) {to[++tot]=y,next[tot]=final[x],final[x]=tot;}
void dfs(int x,int fa)
{
L[x]=++stm;
f[x][0]=fa; for (int i=1; i<19; i++) f[x][i]=f[f[x][i-1]][i-1];
dep[x]=dep[fa]+1;
for (int i=final[x]; i; i=next[i]) if (to[i]!=fa) dfs(to[i],x);
R[x]=stm;
}
int lca(int x,int y)
{
if (dep[x]<dep[y]) swap(x,y);
for (int i=18; i>=0; i--) if (dep[f[x][i]]>=dep[y]) x=f[x][i];
if (x==y) return x;
for (int i=18; i>=0; i--) if (f[x][i]!=f[y][i]) x=f[x][i],y=f[y][i];
return f[x][0];
}
void calcAns(int x,int y,int lca)
{
ans+=treeSum(t1,lca);
ans+=sum(t2,L[x])+sum(t2,L[y])-sum(t2,L[lca])*2;
}
int main()
{
cin>>n;
for (int i=1; i<n; i++)
{
int u,v;
scanf("%d %d\n",&u,&v);
link(u,v),link(v,u);
}
dfs(1,0);
cin>>m;
for (int i=1; i<=m; i++)
{
int u,v; scanf("%d%d",&u,&v);
int g=lca(u,v);
ans=0;
calcAns(u,v,g);
printf("%d\n",ans+app[g]);
app[g]++;
change(t1,L[u],1);
change(t1,L[v],1);
change(t1,L[g],-2);
change(t2,L[g],1);
change(t2,R[g]+1,-1);
}
return 0;
}