赛前复习记录
9.22
- CF1C,成功用正确思路写出了一份错误代码。
9.23
(ps:我怎么感觉这两天的 CF 题都属于是在手动帮我恶补高中数学?)
9.24
- 试题链接:click here
T1
题目描述
题目描述
小 Y 酷爱的接龙游戏正是这样。玩腻了成语接龙之后,小 Y 决定尝试无平方因子二元合数接龙,规则如下:
现有 \(n\) 个不超过 \(10^6\) 的合数,每个均可表示为 \(a=p\times q\)(\(p,q\) 为两个互异素数)。
若 \(a=p_1\times q_1(p_1<q_1)\),\(b=p_2\times q_2(p_2<q_2)\),当且仅当 \(q_1=p_2\) 时 \(b\) 能接在 \(a\) 后面。
请问从给定的这 \(n\) 个数中选数接龙,最长可以形成一个包含多少数的接龙序列?
输入格式
第一行输入一个正整数 \(n\),意义如题干所述。(\(n\le50000\))
第二行输入 \(n\) 个不超过 \(10^6\) 的合数。
输出格式
输出仅一个整数,表示问题的答案。
-
读题,容易发现可以依靠这个素数的限制条件来建图,而且建出来是一个 \(\mathtt{DAG}\) (有向无环图);
-
但鉴于我的图论太垃了,所以我选择不用图论。观察限制条件可以发现,如果找到符合条件的序列,除了相邻两个数有一个共同质因数以外,还有一个条件就是这个序列一定是严格单调递增的。
所以首先就可以对原序列 \(\mathtt{sort}\) 一下。
考虑 \(\mathtt{DP}\),设计状态 \(f_i\) 为以第 \(i\) 个质数为开头的序列往后最长能接多长。从后往前扫描排序过的序列,对每一个数分解质因数为 \(a_i=p_a\times p_b(p_a<p_b)\),那么显然就有一个转移为 \(f_a=\max(f_a,f_b+1)\);
AC code
#include<bits/stdc++.h>
using namespace std;
inline int read(){
int s=0,f=1;
char ch=getchar();
while(!isdigit(ch)){
if(ch=='-') f=-1;
ch=getchar();
}
while(isdigit(ch)){
s=s*10+int(ch-'0');
ch=getchar();
}
return s*f;
}
#define re register
const int N=5e4+10;
const int M=1e6+10;
int n;
int a[N];
int p[N<<1],st[M],tot=0;
int len[N<<1];
int ans=0;
void pre(){
for(re int i=2;i<M;++i){
if(!st[i])
p[++tot]=i,st[i]=tot;
for(re int j=1;j<=tot && p[j]*i<M;++j){
st[p[j]*i]=-1;
if(i%p[j]==0)
break;
}
// cout<<i<<" "<<st[i]<<endl;
}
return ;
}
int main(){
// freopen("chain.in","r",stdin);
// freopen("chain.out","w",stdout);
pre();
// cout<<tot<<endl;
n=read();
for(re int i=1;i<=n;++i)
a[i]=read();
sort(a+1,a+n+1);
int x,y;
for(re int i=n;i;--i){
for(re int j=1;j<=tot;++j){
if(a[i]%p[j]==0){
x=j,y=st[a[i]/p[j]];
break;
}
}
// if(x>y) swap(x,y);
len[x]=max(len[x],len[y]+1);
ans=max(len[x],ans);
}
printf("%d",ans);
return 0;
}
T3
题目描述
题目描述
给出两个数 \(A,B(B\ge A)\),问有多少个序列满足以下条件:
- 序列是递增的。
- 所有数字属于区间 \([A,B]\)(包括 \(A\) 和 \(B\) , \(B−A\le 100\)) 。
- 序列中的所有数字两两互质。
输入格式
一行输入两个数 \(A,B\),其中 \(1\le A\le B\le 10^{18},B-A\le 100\)。
输出格式
输出对应的答案。
-
考场上没怎么细看,写了个 \(\mathtt{O(2^{B-A})}\) 的暴力,但是挂了;
-
状压 \(\mathtt{DP}\)。观察数据范围可以发现,由于 \(B\) 和 \(A\) 的差最多只有 \(100\),说明会在这段区间中重复出现的质因数在 \(100\) 以内,而 \(100\) 以内的质因数只有 \(25\) 个,考虑状压。
-
首先对于 \(70\%\) 的数据,有 \(B-A\le 50\)。此时只有 \(15\) 个质数需要考虑。设 \(f_{i,j}\) 表示考虑到第 \(i\) 个数(对应 \(A+i\)),序列里的数包含的质因数状态为 \(j\) 时共有多少个不同序列。
那么显然有当不选这第 \(i\) 个数时,\(f\) 数组直接从 \(i-1\) \(\mathtt{copy}\) 过来。
而当要选择第 \(i\) 个数时,设 \(p\) 表示这个数包含的质因数情况,那么从 \(p\) 相对于全集 \(U\) 的补集 \(v\) 中考虑转移(因为要求两两互质,说明整个序列中每个质因数至多被一个数包含),就有 \(f_{i,s|p}+=f_{i-1,s}\),\(s\) 表示 \(v\) 的任意子集。 -
对于 \(100\%\) 的数据,
由于没有离谱的 Hack 点,所以在每个区间中总会出现一些质因数只被一个数包含。那么这些数可以不用考虑,状压范围减小。
AC code
#include<bits/stdc++.h>
using namespace std;
#define ll long long
const int N=(1<<23)+10;
ll a,b;
int n;
int t=25;
int p[30]={2,3,5,7,11,13,17,19,23,29,31,37,41,43,47,53,59,61,67,71,73,79,83,89,97};
int f[N];
int cnt[30];
int num[105];
int main(){
scanf("%lld%lld",&a,&b);
n=b-a+1;
for(int i=0;i<=n;++i){
ll x=a+i;
for(int i=0;i<t;++i)
if(x%p[i]==0)
++cnt[i];
}
int dx=0;
for(int i=0;i<t;++i){
if(dx) p[i-dx]=p[i];
if(cnt[i]<=1) ++dx;
}
t-=dx;
for(int i=0;i<n;++i){
ll x=a+i;
for(int j=0;j<t;++j)
if(x%p[j]==0)
num[i]|=1<<j;
}
f[0]=1;
int u=(1<<t)-1;
// cout<<u<<endl;
for(int i=0;i<n;++i){
int v=u^num[i];
for(int j=v;j;j=(j-1)&v)
f[j|num[i]]+=f[j];
f[num[i]]+=f[0];
}
ll ans=0;
for(int i=0;i<=u;++i)
ans+=f[i];
printf("%lld",ans-1);
return 0;
}
9.27
先说一下,虽然初赛出分了,但我没敢看
9.28
查了分,看来暂时我还能不退役
- 完成了 CF3D。
9.29
- 完成了 CF4D。

浙公网安备 33010602011771号