赛前复习记录

9.22

  • CF1C,成功用正确思路写出了一份错误代码。

9.23

  • 修改了昨日 CF1C,发现问题是 \(eps\) 写太大了……

  • CF2C,目前被技术性难题卡住。

  • P1549,已完成。使用了奇怪的 \(\mathtt{DFS}\)

(ps:我怎么感觉这两天的 CF 题都属于是在手动帮我恶补高中数学?)

9.24

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)\),问有多少个序列满足以下条件:

  1. 序列是递增的。
  2. 所有数字属于区间 \([A,B]\)(包括 \(A\)\(B\)\(B−A\le 100\)) 。
  3. 序列中的所有数字两两互质。

输入格式

一行输入两个数 \(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

先说一下,虽然初赛出分了,但我没敢看

  • 完成了之前 CF2C.

  • 开始写 CF3D 了。

9.28

查了分,看来暂时我还能不退役

9.29

posted @ 2022-09-23 19:33  Star_LIcsAy  阅读(266)  评论(2)    收藏  举报