CSP模拟52联测14

A.长春花

简单题。打表发现情况并不多,记录下平方后模 \(p\) 对应的值,然后枚举 \(a\),用链表维护即可。

点击查看代码
#include<bits/stdc++.h>
using ll=long long;using ull=unsigned long long;
int a[100005],b[100005],p,ans,mx;
bool vis[100005];
std::list<int> l;
std::stack<std::list<int>::iterator> stk;
int main(){
	std::cin.tie(nullptr)->sync_with_stdio(false);
	std::cin>>p;
	for(int i=0;i<=p-1;++i){
		int mo=1ll*i*i%p;
		l.push_back(i);
		if(!vis[mo])vis[mo]=true,a[i]=b[i]=mo,mx=i;
	}
	for(int i=0,lst;i<=mx;++i){
		for(auto it=l.begin();it!=l.end();++it){
			lst=(*it-a[i]+p)%p;
			if(vis[lst])stk.push(it),ans=i;
		}
		while(!stk.empty())l.erase(stk.top()),stk.pop();
	}
	std::cout<<ans;
	return 0;
}

B.紫罗兰

无向无权图求最小环的个数。

考场上用 Floyd 找最小环,只有 55pts。

正解是以每个点为源点 bfs。把最短路拼成最小环,记录方案数。

根据 bfs 的性质,队列中的元素任何时刻至多只有两个层次的节点。我们可以判断当前搜索的节点是否被访问过。

如果当前搜索的节点未被访问过就更新最短路和方案数,否则就更新下最小环的长度和个数。虽然不能保证该环为简单环,但所有情况拼成的环中一定存在简单环,所以对所有的环长度取 \(\min\) 就一定是最小环。

这样求出的长度为 \(L\) 的最小环会被重复搜 \(L\) 遍,所以答案要除以 \(L\)\(L\) 为奇数时,\(dis_i=dis_j\) 会被算两次,还要除以 \(2\)

直接粘的 chancelong 赛时的代码加了点注释。

点击查看代码
#include<bits/stdc++.h> 
#define int long long
using namespace std;
vector<int>edge[3010];
int n,m;
int minn=2e9+10;
int tot=0;
int dis[3010],len[3010];//dis:源点到点i的最短路 len:源点到点i最短路的方案数 
void search(int x){
	queue<int>q;
	memset(dis,0x3f,sizeof(dis));
	memset(len,0,sizeof(len));
	q.push(x);
	dis[x]=0,len[x]=1;
	while(!q.empty()){
		int i=q.front();
		q.pop();
		for(auto j:edge[i]){//i->j
			// 搜索到环: x->...i->j->...x 
			int turnd=dis[i]+dis[j]+1;//环的长度 
			int turnc=len[i]*len[j];//求拼成环的方案数 
			if(dis[j]==dis[i]||dis[j]==(dis[i]+1)){//j节点已被访问过 
				if(turnd<minn){
					minn=turnd;
					tot=turnc;
				}
				else if(turnd==minn){
					tot+=turnc;
				}
			}
			if(dis[j]>dis[i]+1){
				dis[j]=dis[i]+1;
				len[j]=len[i];
				q.push(j);
			}
			else if(dis[j]==dis[i]+1){
				len[j]+=len[i];
			}
		}
	}
}
signed main(){
	cin>>n>>m;
	for(int i=1;i<=m;i++){
		int x,y;
		cin>>x>>y;
		edge[x].push_back(y);
		edge[y].push_back(x);
	}
	for(int i=1;i<=n;i++){
		search(i);
	}
	tot/=minn;
	if(minn%2)tot/=2;
	cout<<tot;
}

C.天竺葵

这基本就是 LIS 吧......

朴素的转移方程:\(f_i=\max_{1\le j<i}\{f_j+1\}(a_j b_{f_j}<a_i)\)

这样直接做是 \(O(n^2)\) 的。

考虑像 LIS 一样的优化,贪心地想,末尾肯定是越小越容易往后接东西。所以定义 \(f_i\) 为长度为 \(i\) 的子序列末尾 \(a b_i\) 的最小值。转移的时候直接二分查找转移点即可,复杂度 \(O(n\log n)\)

点击查看代码
#include<bits/stdc++.h>
using ll=long long;using ull=unsigned long long;
int n,ans;
ll a[1000005],b[1000005],f[1000005];
int main(){
	std::cin.tie(nullptr)->sync_with_stdio(false);
	std::cin>>n;
	for(int i=1;i<=n;++i)std::cin>>a[i];
	for(int i=1;i<=n;++i)std::cin>>b[i];
	memset(f,0x3f,sizeof f);
	for(int i=1;i<=n;++i){
		int pos=std::lower_bound(f+1,f+1+ans,a[i])-f;
		ans=std::max(ans,pos);
		f[pos]=std::min(f[pos],b[pos]*a[i]);
	}
	std::cout<<ans;
	return 0;
}

D.风信子

类似于 超级钢琴。那道题是把询问元组放入堆中,每次取最大值,根据答案的位置把询问分裂开再放入堆中维护。

我直接搬官方题解吧(

这场暴力分比较多,T1 T3比较简单。T2 边权为 \(1\) 找环应该想到 bfs,但不知道为啥用 Floyd 只有 55。我估的 70 啊www

T4 部分分应该有 65。但 \(k=1\) 拿线段树维护的 15 分没打完。

posted @ 2023-10-11 21:21  Flandreqwq  阅读(89)  评论(1)    收藏  举报