9.22-CSP-S模拟9

T1 最长上升子序列

这玩意是拍过去的,猜了一个小时结论,改一次拍一次。做法大概就是从小到达考虑还没放的数,如果当前最优最长上升子序列里有比它大的项,那么它可以塞到它后边,然后更新最优上升子序列,否则就塞到最后一个位置的前边。对于塞到前边的数,需要让他们降序排列,保证每一段最多只能给LIS贡献1

点击查看代码
#include <bits/stdc++.h>
typedef long long ll;typedef unsigned long long ull; typedef double db;typedef long double ldb;
#define fre(x) freopen(#x ".in","r",stdin),freopen(#x ".out","w",stdout)
#define Rep(i,a,b) for(int i=a;i<=b;++i) 
#define Dwn(i,a,b) for(int i=a;i>=b;--i)
#define pii pair<int,int>
#define mair make_pair
#define fir first
#define sec second
using namespace std;

const int maxn=2e5+10;

int a[maxn],b[maxn];
int n,K;
vector<int>vec1[maxn],vec2[maxn];
bool vis[maxn];

void Sol(int x){
	int pos=upper_bound(a+1,a+K+1,x)-a;
	if(a[pos]>x){
		a[pos]=x;
		vec1[pos].push_back(x);
		return;
	}
	pos-=2;
	vec2[pos].push_back(x);
}

void solve(){
	cin>>n>>K;
	Rep(i,1,K)cin>>a[i],vis[a[i]]=true,b[i]=a[i];
	Rep(i,1,n)if(!vis[i]){
		Sol(i);
	}
	Rep(i,0,K){
		if(!vec1[i].empty()){
			sort(vec1[i].begin(),vec1[i].end());
			for(auto it  : vec1[i])cout<<it<<" ";
		}
		if(i<K){
			vec2[i].push_back(b[i+1]);
			if(!vec2[i].empty()){
				sort(vec2[i].begin(),vec2[i].end());
				reverse(vec2[i].begin(),vec2[i].end());
				for(auto it  : vec2[i])cout<<it<<" ";
			}
		}
	}
}

int main (){ ios::sync_with_stdio(false),cin.tie(0),cout.tie(0);return solve(),0; }

T2 独特序列

应该是最简单的题,考虑设 \(f_i\) 表示以 \(i\) 结尾,不考虑后边的数时的独特序列数量,维护 \(pre_i\)\(nxt_i\) 表示 \(a_i\) 上一次和下一次出现的位置。

对于 \(f_i\) 能转移到它的 \(f_j\) 需满足 \(j<i ,pre_i \leq j,i \leq nxt_j\),到这里是个裸的三维偏序实际上已经可以直接莽 CDQ 了,\(2e5\) 跑俩log轻轻松松,

但是实质上可能合法的 \(j\) 的位置是连续的,前两维已经保证了于是只需要再上个树限制第三维,随便莽个主席树就行了,码量甚至可能比 CDQ 还大?

点击查看代码
#include <bits/stdc++.h>
typedef long long ll;typedef unsigned long long ull; typedef double db;typedef long double ldb;
#define fre(x) freopen(#x ".in","r",stdin),freopen(#x ".out","w",stdout)
#define Rep(i,a,b) for(int i=a;i<=b;++i) 
#define Dwn(i,a,b) for(int i=a;i>=b;--i)
#define pii pair<int,int>
#define mair make_pair
#define fir first
#define sec second
#define int ll
using namespace std;

const int maxn=2e5+10,Mod=998244353;

struct ZXS{
	#define LCH tr[rt].lch
	#define RCH tr[rt].rch
	struct Tree{int lch,rch,val;}tr[maxn*40];
	int tot;int root[maxn];
	void Pushup(int rt){tr[rt].val=(tr[LCH].val+tr[RCH].val)%Mod;}
	void Modify(int &rt,int p,int l,int r,int x,int w){
		if(!rt)rt=++tot,tr[rt]=tr[p];
		if(l==r)return tr[rt].val=(tr[rt].val+w)%Mod,void();
		int mid=(l+r)>>1;
		if(x<=mid){LCH=0;Modify(LCH,tr[p].lch,l,mid,x,w);}
		else {RCH=0;Modify(RCH,tr[p].rch,mid+1,r,x,w);}
		Pushup(rt);
	}
	int Query(int rt,int p,int l,int r,int s,int t){
		if(!rt)return 0;
		if(s<=l && t>=r)return (tr[rt].val-tr[p].val)%Mod;
		int mid=(l+r)>>1,res=0;
		if(s<=mid)res=Query(LCH,tr[p].lch,l,mid,s,t);
		if(t>mid)res=(res+Query(RCH,tr[p].rch,mid+1,r,s,t))%Mod;
		return res;
	}
}T;

int n;
int a[maxn],pre[maxn],nxt[maxn],last[maxn];
int f[maxn];

/*void Debug(){
	Rep(i,1,n)cerr<<pre[i]<<" ";cerr<<"\n";
	Rep(i,1,n)cerr<<nxt[i]<<" ";cerr<<"\n";
	Rep(i,1,n)cerr<<f[i]<<" ";cerr<<"\n";
}*/

void solve(){
	cin>>n;
	Rep(i,1,n){ cin>>a[i]; pre[i]=last[a[i]]; last[a[i]]=i; }
	Rep(i,1,n)last[i]=n+1;
	Dwn(i,n,1){ nxt[i]=last[a[i]];last[a[i]]=i; }
	f[1]=1;
//	T.Modify(T.root[0],T.root[0],0,n+1,0,1);
	T.Modify(T.root[1],T.root[0],0,n+1,nxt[1],1);
	Rep(i,2,n){
		if(pre[i]==0){
			f[i]=T.Query(T.root[i-1],T.root[pre[i]],0,n+1,i,n+1)+1;
		}else f[i]=T.Query(T.root[i-1],T.root[pre[i]-1],0,n+1,i,n+1);
		T.Modify(T.root[i],T.root[i-1],0,n+1,nxt[i],f[i]);
	}
	int ans=0;
	Rep(i,1,n){
		if(nxt[i]==n+1)ans=(ans+f[i])%Mod;
	}
//	Debug();
	cout<<(ans+Mod)%Mod<<"\n";
}
#undef int
int main (){ ios::sync_with_stdio(false),cin.tie(0),cout.tie(0);return solve(),0; }


T3 最大GCD

裸题,赛时脑子抽了写了个树状数组求前缀和,甚至连修改都没有。算法本身就是基于值域的,随便上个桶就能判了,赛时像个rz。

直接暴力去判能不能让所有数都能整除g,也就是让每个数都增加到离它最近的g的倍数,显然是可以按照g的倍数分成 \(n/g\) 段,每段分别算一下贡献就行。枚举倍数是一个log的,查询时就是查个前缀和做差。

点击查看代码
#include <bits/stdc++.h>
typedef long long ll;typedef unsigned long long ull; typedef double db;typedef long double ldb;
#define fre(x) freopen(#x ".in","r",stdin),freopen(#x ".out","w",stdout)
#define Rep(i,a,b) for(int i=a;i<=b;++i) 
#define Dwn(i,a,b) for(int i=a;i>=b;--i)
#define pii pair<int,int>
#define pil pair<int,ll>
#define mair make_pair
#define fir first
#define sec second
using namespace std;

const int maxn=3e5+10;

int N;
int n;
ll Lim,Sum;
int a[maxn];
int ans=1;
pil sum[maxn];

void Check(){
	ll ned=1LL*n*N-Sum;
	if(ned<=Lim){
		Lim-=ned;
		cout<<((ll)N)+(Lim)/n<<"\n";
		exit(0);
	}else return;
}

bool Try(int x){
	pil last=mair(0,0);
	ll ned=0;
	for(int j=1;(j-1)*x<N;++j){
		pil res=sum[min(N,j*x)];
		ned+=(1LL*(res.fir-last.fir)*j*x-res.sec+last.sec);
		last=res;
		if(ned>Lim)return false;
	}
	return ned<=Lim;
}

void solve(){
	cin>>n>>Lim;Rep(i,1,n)cin>>a[i],N=max(a[i],N),Sum+=a[i];
	Check();
	Rep(i,1,n)sum[a[i]].fir++,sum[a[i]].sec+=a[i];
	Rep(i,1,N)sum[i].fir+=sum[i-1].fir,sum[i].sec+=sum[i-1].sec;
	Dwn(i,N-1,2)if(Try(i))return cout<<i<<"\n",void();
	cout<<1<<"\n";
}

int main (){ ios::sync_with_stdio(false),cin.tie(0),cout.tie(0);return solve(),0; }


T4 连续子段

赛时没想到逆序对,不会内部排序就挂了。根据提示和感性分析把所有需要的数挪到一起是比较好算的。做法显然是状压,考虑每一个数是否被选,如果选就贡献逆序对数,不选就是把它扔掉的步数,显然扔空位时是从外往内逐个扔,扔它时两边肯定全都是被选数,挑个近的边界移出去就行

点击查看代码
#include <bits/stdc++.h>
typedef long long ll;typedef unsigned long long ull; typedef double db;typedef long double ldb;
#define fre(x) freopen(#x ".in","r",stdin),freopen(#x ".out","w",stdout)
#define Rep(i,a,b) for(int i=a;i<=b;++i) 
#define Dwn(i,a,b) for(int i=a;i>=b;--i)
#define pii pair<int,int>
#define mair make_pair
#define fir first
#define sec second
#define lowbit(x) (x&-x)
using namespace std;

const int maxn=2e2+10,maxk=17,INF=0x3f3f3f3f;

int n,K;
int a[maxn];
vector<int>vec[maxn],Now;
int ans=INF;
int Rand(int l,int r){return l+(rand())%(r-l+1);}

void Check(){
	Rep(i,1,n-K+1){
		int res=0;
		for(int j=0;j<K;++j){
			res+=abs(Now[j]-(i+j));
		}
		ans=min(ans,res);
	}
}

void Dfs(int step){
	if(step>K)return Check();
	for(auto it : vec[step]){
		Now.push_back(it);
		Dfs(step+1);
		Now.pop_back();
	}
}

void solve(){
	srand(time(NULL));
	cin>>n>>K;
	Rep(i,1,n)cin>>a[i],vec[a[i]].push_back(i);
	if(n==K)Dfs(1),cout<<ans/2<<"\n";
	else cout<<Rand(0,2)<<"\n";
}

int main (){ ios::sync_with_stdio(false),cin.tie(0),cout.tie(0);return solve(),0; }

posted @ 2022-09-22 14:39  Delov  阅读(45)  评论(0编辑  收藏  举报