首页 > 其他分享 >暑假集训CSP提高模拟 16

暑假集训CSP提高模拟 16

时间:2024-08-08 21:28:15浏览次数:8  
标签:200001 最长 16 int len CSP 250001 序列 集训

\[暑假集训CSP提高模拟 \lim_{x\rightarrow\infty}\frac{8f_{x}}{f_{x+1}}\times(\sqrt{5}+1),\ \forall f_{x}=f_{x-1}+f_{x-2} \]

如果你实在不会算 \(\forall f_{x}=f_{x-1}+f_{x-2}\) 的情况,那你可以把它特化成斐波那契数列. 如果你连斐波那契数列特化的上式都算不出来,那么你应该看 这一篇 的第 7.7 章

A.九次九日九重色

今天先去研究了一下 \(n\log n\) 的最长上升子序列和最长公共子序列怎么写:

根据贪心思路,我们可以将最长上升子序列问题的求解优化至 \(n\log n\)

考虑到对于每次转移,我们都可以记录下当前序列最末尾的数字,可以想到的是,当前序列末尾数字越小,则可能被填入子序列的数字就越多,因此末尾更小的序列可能就是决策更优的,因此我们每次都保留这样的序列向后拓展.

也就是,我们使用数组 \(f_{i}\) 表示当枚举到第 \(i\) 位时,全部最长上升子序列中,最小的末尾元素值,则可以发现:

  • 当新的元素值更大的时候,符合上升条件,直接填到末尾
  • 当新的元素值更小的时候,虽然不符合上升条件,但是符合更新条件,可能会导致更优的决策,因此我们向前找到第一个能放当前值的 \(i\) 进行更新.

代码如下:

#include<bits/stdc++.h>
using namespace std;
int n;
int a[100001];
int f[100001];
int main(){
	ios::sync_with_stdio(false);
	cin>>n;
	for(int i=1;i<=n;++i){
		cin>>a[i];
	}
	memset(f,0x3f,sizeof f);
	f[1]=a[1];
	int len=1;
	for(int i=2;i<=n;++i){
		int l=0,r=len;
		if(a[i]>f[len]){
			f[++len]=a[i];
		}
		else{
			f[lower_bound(f+1,f+len+1,a[i])-f]=a[i];
		}
	}
	cout<<len<<endl;
}

字序列是一个字符串中有序的一段,即序列中的每个数在原序列内都从左到右排列,公共子序列即为几个字符串都含有的子序列.

\[dp[i][j]=\begin{cases}dp[i-1][j-1]+1\qquad (x[i]==y[j]) \\max \begin{cases} dp[i][j-1]\\dp[i-1][j] \end{cases} \qquad(x[i] \neq y[j]) \end{cases} \]

类似最长公共子串,但不一样的是,假如 \(x[i] \neq y[j]\),不要急着清零,因为最长公共子序列并不需要严格相邻. 此时应该跳过不相等的内容,那么如何跳过呢? 我们采用了继承相邻状态的方法.

假如序列中的每个数都仅仅出现过一次,那么我们可以考虑对上述 \(n^{2}\) 算法进行优化.

A:3 2 1 4 5
B:1 2 3 4 5

我们不妨给它们重新标个号:把 \(3\) 标成 \(a\),把 \(2\) 标成 \(b\),把 \(1\) 标成 \(c\),以此类推,目的是将 \(A\) 变为一个递增的序列,于是变成:

A: a b c d e
B: c b a d e

这样标号之后,最长公共子序列长度显然不会改变. 但是出现了一个性质:
两个序列的子序列,一定是 \(A\) 的子序列. 而A本身就是单调递增的,因此这个子序列是单调递增的

换句话说,只要这个子序列在 \(B\) 中单调递增,它就是 \(A\) 的子序列

哪个最长呢?当然是 \(B\) 的最长上升子序列最长
因此,我们只需要在 \(n\log n\) 的时间复杂度内求出 \(B\) 的最长上升子序列即可

因此这个题也可以转化成最长公共子序列问题,只不过在这里我们的 “公共” 是整除

#include<bits/stdc++.h>
using namespace std;
int n;
int p[200001],q[200001];
int a[200001];
int f[200001];
struct pai{
	int a,b;
	bool operator <(const pai &A)const{
		if(a==A.a) return b>A.b;
		return a<A.a;
	}
};
vector<pai>v;
map<int,int>mp;
int posp[200001],posq[200001];
int c[200001];
int lowbit(int x){return x&-x;}
int ask(int x){
	int ans=0;
	if(x<=0) return 0;
	while(x){
		ans=max(ans,c[x]);
		x-=lowbit(x);
	}
	return ans;
}
void update(int x,int val){
	if(x<=0) return;
	while(x<=n){
		c[x]=max(c[x],val);
		x+=lowbit(x);
	}
}
vector<int>pos[200001];
int main(){
	ios::sync_with_stdio(false);
	cin>>n;
	for(int i=1;i<=n;++i){
		cin>>p[i];
		posp[p[i]]=i;
	}
	for(int i=1;i<=n;++i){
		cin>>q[i];
		posq[q[i]]=i;
	}
	for(int i=1;i<=n;++i){
		for(int j=i;j<=n;j+=i){
			if(posq[j]) pos[i].push_back(posq[j]);
		}
//		for(int i:pos[i]) cout<<i<<" ";cout<<endl;
		sort(pos[i].begin(),pos[i].end());
		reverse(pos[i].begin(),pos[i].end());
	}
	int ans=0;
	for(int i=1;i<=n;i++){
		for(auto v:pos[p[i]]){
			int j=v;
			f[j]=ask(j);
			f[j]=max(ask(j-1)+1,f[j]);
			ans=max(ans,f[j]);
			update(j,f[j]);
		}
	}
	cout<<ans<<endl;
//	sort(v.begin(),v.end());
//	for(int i=0;i<=v.size()-1;++i){
//		cout<<v[i].a<<" "<<v[i].b<<endl;
//		a[i+1]=v[i].b;
//	}
//	memset(f,0x7f,sizeof f);
//	f[1]=a[1];
//	int len=1;
//	for(int i=2;i<=n;++i){
//		int l=0,r=len;
//		if(a[i]>f[len]){
//			f[++len]=a[i];
//		}
//		else{
//			f[lower_bound(f+1,f+len+1,a[i])-f]=a[i];
//		}
//	}
//	cout<<len<<endl;
}

B.天色天歌天籁音

写的普通莫队,赛时我在大黄旁边看他写分块,数组开了快一个 G,给我急死了

这是用莫队求区间众数的基本套路:先开一个桶,再开一个桶的桶,用来记录什么时候该转移了. 可以注意到莫队每次进行转移都只会至多使一个桶的值变化 \(1\),因此若当前数量的桶的数量为零,则说明需要给众数减一,否则,若新加入的桶的数量是更大的,则更新最优答案.

还是说一下这个题为什么是区间众数吧,注意到我们每找出一个上升子序列,都会贡献 \(1\) 的答案. 因此实际上这个题的目标是让我们将原数列分成若干单调递增的子序列,最小化切分总数量. 因此可以考虑每次都贪心地从小到大能拿就拿. 最后剩下的一定是出现次数最多的,即只有众数会对答案有影响.

#include<bits/stdc++.h>
using namespace std;
int n,m;
int len,locate[250001];
struct ques{
	int id,l,r;
	bool operator <(const ques &A)const{
		if(locate[l]!=locate[A.l]) return locate[l]<locate[A.l];
		if(locate[l]&1) return r<A.r;
		return r>A.r;
	}
}q[250001];
int a[250001],cnt[250001],totcnt[250001],ans;
map<int,int>mp;int mpcnt=0;
int nowl,nowr;
int anss[250001];
inline void prew(){
	len=sqrt(m);
	if(!m) len=sqrt(n);
	for(int i=1;i<=n;++i){
		locate[i]=i/len+(i%len!=0);
	}
}
inline void reduce(int pos){
	totcnt[cnt[a[pos]]]--;
	if(totcnt[cnt[a[pos]]]==0 and ans==cnt[a[pos]]) ans--;
	cnt[a[pos]]--;
//	cout<<"reduce "<<pos<<": "<<ans<<endl;
}
inline void add(int pos){
	cnt[a[pos]]++;
	if(totcnt[cnt[a[pos]]]==0 and ans<cnt[a[pos]]) ans=cnt[a[pos]];
	totcnt[cnt[a[pos]]]++;
//	cout<<"add "<<pos<<": "<<ans<<endl;
}
int main(){
//	freopen("大样例/T2.in","r",stdin);
//	freopen("out.out","w",stdout);
	scanf("%d %d",&n,&m);
	for(int i=1;i<=n;++i){
		scanf("%d",&a[i]);
		if(!mp.count(a[i])){
			mp[a[i]]=++mpcnt;
			a[i]=mpcnt;
		}
		else a[i]=mp[a[i]];
	}
	for(int i=1;i<=m;++i){
		scanf("%d %d",&q[i].l,&q[i].r);
		q[i].id=i;
	}
	prew();
	sort(q+1,q+m+1);
	nowl=q[1].l;nowr=q[1].l;
	ans=1;cnt[a[q[1].l]]++;totcnt[cnt[a[q[1].l]]]++;
	for(int i=1;i<=m;++i){
		while(nowl<q[i].l) reduce(nowl++);
		while(nowl>q[i].l) add(--nowl);
		while(nowr<q[i].r) add(++nowr);
		while(nowr>q[i].r) reduce(nowr--);
		anss[q[i].id]=ans;
	}
	for(int i=1;i<=m;++i){
		printf("%d\n",-anss[i]);
	}
}

标签:200001,最长,16,int,len,CSP,250001,序列,集训
From: https://www.cnblogs.com/HaneDaCafe/p/18349777

相关文章

  • [赛记] 暑假集训CSP提高模拟16
    $Peppa\Pig$都有时间写赛记了,看来现在这题是真不好改了今天又是一题没切;九次九日九重色0pts原题:现找的赛时理解错了子序列,给理解成了字串($HDK$给我说的,要不我可能还不知道),导致大样例咋手模都出不来,干了45min,整了个不像暴力的暴力然后走了;赛后证明,我的乱胡搞到了$......
  • CSP16
    这题,唯一坑点,子序列是不连续的注意,子序列可以不连续,子串必须连续。有一个很显然的暴力点击查看代码intdp[N][N],n,p[N],q[N];intmain(){ speed(); freopen("in.in","r",stdin); freopen("out.out","w",stdout); cin>>n; for(inti=1;i<=n;i++)cin>>......
  • GMOJ 8101. 【2024年SD省队集训Day8】 正交向量
    效率时间复杂度:\(O(Tn\times3^9\times9)\)。没有任何卡常,能在\(1.08\)s内过hack.txt,而CHJ的代码在同样情况下跑了\(39\)s,LZY要用\(34\)s,PWX要用\(75\)s。但是在GMOJ上要用\(770\)ms,是目前比较劣的解。思路以下关于数字的第几位都是从\(0\)开始,从最低位到最......
  • 2024北京集训trick合集
    atcoderARC092F给定一张\(n\)个点\(m\)条边的有向图,判断每一条边反向后是否改变图中强连通分量的数量。数据范围:\(n\le1000\\\\m\le200000\)先跑一遍tarjan,然后问题转化为判断每个直接相连的两点在不经过其连边的情况下是否互通。对每个点dfs维护前缀和后缀能否回......
  • 暑假集训CSP提高模拟16
    \(\color{skyblue}9-nine-\)专场(小阳历最水的一回。日常RE挂分。我这是不是风水不好?(逃)9-nine-九次九日九重色赛时以为是连续的...大样例模了半天没模懂...按连续的思路打了个二分上去...还骗了30pts?(官方题解按照官方题解写的Code,但WA#1,不知道哪错了...intn,a[N],b[N......
  • 暑假集训CSP提高模拟16
    1.九次九日九重色一开始做的时候被题面给迷惑住了,没想到可以跳着匹配(样例太水)。那我们来考虑如何做,首先思路肯定是把能匹配的暴力求出来,根据不知道怎么搞的调和计数,这样的复杂度还不是很高,是\(O(NlogN)\),可以搞。观察一下预处理出来的序列,是不是很熟悉。没错剩下的就是求最......
  • Xcode 16 beta 5 (16A5221g) 发布 - Apple 平台 IDE
    Xcode16beta5(16A5221g)发布-Apple平台IDEIDEforiOS/iPadOS/macOS/watchOS/tvOS/visonOS请访问原文链接:https://sysin.org/blog/apple-xcode-16/,查看最新版。原创作品,转载请保留出处。Xcode16的新功能使用预测代码补全功能和更快的预览功能,将奇思妙想转化为代码......
  • CSP模拟 小 trick 总结 (持续施工中)
    虽然这篇博客来的有点晚,但还是写了,欢迎dalao补充(1、分块、莫队有关:(1):一个真正的回滚莫队(感谢Qyun的讲解)$\\\\\\\\$学习回滚莫队的时候,我们经常会在回滚时使用memcpy来恢复以前的版本,但众所周知--memset和memcpy常数巨大,破坏了莫队$O(n\sqrtn)$的时间复杂度,导......
  • 暑假集训csp提高模拟16
    赛时rank38,T120,T250,T30,T40T2挂分原因:莫队n,m写反了,但样例中这俩一样,遂寄T1九次九日九重色\[\color{Green}{\Huge{!!!!!!!!!九日\,!!!!!!!!!}}\]\[\color{Red}{\Huge{!!!!!!!!!九日\,!!!!!!!!!}}\]\[\color{Blue}{\Huge{!!!!!!!!!九日\,!!!!!!!!!}}\]\[\color{Yellow}{\......
  • 「模拟赛」暑期集训CSP提高模拟15(8.7)
    赛时记录:开场看T1,一眼\(manacher\)求子串回文串,听课是听了,但是不会啊。跳了。看T2,发现结论题,推了会结论打上走了。打完T2想了会T3、T4,无思路,回来打了T1暴力和特殊性质,\(30pts\),又去想T3,还是不会,这时还剩一个小时。不行,现在才\(130pts\),包打祭的啊,能不能翻盘看我T1......