2025 -- 云智计划 -- 【CSP-S】模拟赛 #4&5_总结+题解

模拟赛 4

总结

T1

题意根据特殊情况想到了dp然后很快就写出了单调队列优化版,后来又推出了满分的做法。整体上没有什么问题。

T2

T2考场上没有没有什么思路下来发觉是求LIS,用二分去求,但是发觉此题不可以用二分(具体原因后面会说),吃了不少亏。

题解

T1

我们先可以看一组特殊数据:所有花费代价单调递增。

根据单调递增我们可以想到我们可以去二分参数。

这个时候我们可以推出一个非常简单的DP式子。

\[dp_{i}=\max_{1\le j \le i}(dp_j+i-j+d_i) \]

显然这个式子是可以单调队列优化的。具体优化的过程就不说了。

那么答案就是最小的合法的参数。


这个时候我们来想一下正解:所有花费代价不一定单调递增。我们会发现:如果说我选的是较大的参数。那么如果我要跳较小的位置的话,是一定可以跳到的。

所以我们可以对花费代价做一个后缀min。

而此时它就一定是单调递增的了。

代码

#include<bits/stdc++.h>
#define int long long
using namespace std;
const int maxn=1e6+5;
int a[maxn],b[maxn],dp[maxn],n,t,bit[maxn];
bool check(int x)
{
	memset(dp,0,sizeof(dp));
	deque<int> q;
	q.push_back(n);
	int ans=1e18+5;
	for(int i=n-1;i>=2;i--)
	{
		while(!q.empty()&&q.front()>i+x) q.pop_front();
		dp[i]=dp[q.front()]+a[i];
		while(!q.empty()&&dp[i]<dp[q.back()]) q.pop_back();
		q.push_back(i);
		if(i<1+x) ans=min(ans,dp[i]);
	}
	return ans<=t;
}
signed main()
{
	freopen("journey.in","r",stdin);
	freopen("journey.out","w",stdout);
	ios::sync_with_stdio(0);
	cin.tie(0); cout.tie(0);
	cin>>n>>t;
	t=t-n+1;//我们会发现我们的时间起码都得用从N-1的时间(即从一走到N不计算休息时间的时间)。所以我就在这里提前剪掉了。
	for(int i=1;i<n;i++) cin>>b[i];
	for(int i=1;i<n;i++) cin>>a[i];
	for(int i=n-2;i>=1;i--) b[i]=min(b[i],b[i+1]);
	int l=1,r=n-1,ans=0;
	while(l<=r)
	{
		int mid=(l+r)>>1;
		if(check(mid)) ans=mid,r=mid-1;
		else l=mid+1;
	}
	cout<<b[ans];
	return 0;
}

T2

这种题其实我们曾经做过一道十分类似的。当时那道题是给你N对数。你可以任意选一些数并对它任意排序。求全部单调递增的情况。

我们会发现这道题跟曾经做过的这道题是很相像的。所以我们就照着他同样的办法对他的第一关键字排个序再对第二个关键字求LIS就可以了。


重点:m的情况

对于m我们其实可以马上想到我们用两个数组一个存在字符串内有m的另外一个存在字符串里面没有m的。

但是此时你就会发现中间它会有一些空着的情况。因为是交错的排列的,所以说在 \(dp\) 数组里面很容易就出现空的情况。所以他不满足单调性也就不能二分了。

我们就改成用树状数组优化。

而对于下标的问题,我们直接给它标一个号就可以了。

代码

#include<bits/stdc++.h>
#define inf 0x3f3f3f3f3f3f3f3f
#define int long long
#define endl '\n'
using namespace std;
const long long maxn=2e5+5;
pair<string,int> a[maxn];
int vis[maxn];
string s[maxn];
struct BIT
{
	int n,bit[maxn];
	int lb(int x)
	{
		return x&-x;
	}
	void init(int x)
	{
		n=x;
		for(int i=1;i<=n;i++) bit[i]=0;
	}
	void update(int x,int y)
	{
		while(x<=n) bit[x]=max(bit[x],y),x+=lb(x);
	}
	int query(int x)
	{
		int ans=0;
		while(x) ans=max(ans,bit[x]),x-=lb(x);
		return ans;
	}
}dp[2];
signed main()
{
	freopen("queue.in","r",stdin);
	freopen("queue.out","w",stdout);
	ios::sync_with_stdio(0);
	cin.tie(0); cout.tie(0);
	int n,ans=0;cin>>n;
	for(int i=1;i<=n;i++) cin>>s[i];
	sort(s+1,s+1+n);
	for(int i=1;i<=n;i++)
	{
		a[i].first=s[i],a[i].second=i;
		reverse(a[i].first.begin(),a[i].first.end());
	}
	sort(a+1,a+1+n);
	for(int i=1;i<=n;i++) vis[i]=(a[i].first.find('m')!=-1);
	dp[0].init(n),dp[1].init(n);
	for(int i=1;i<=n;i++)
	{
		int now=dp[!vis[i]].query(a[i].second-1);
		ans=max(ans,now+1);
		dp[vis[i]].update(a[i].second,now+1);
	}
	cout<<ans;
	return 0;
}

模拟赛 5

总结

T1

T1 很快就想到了正解主要是我们曾经讲过的一道题,那道题的做法,给予我了启发。所以很快就切掉了。没有大问题

T2

T2 一开始没有什么思路,后来。把它们这每一个他施工的人要求给他分开就可以了。

T3

T3 在考试的时候只想到了按位拆分的算法。但是具体的就不知道怎么做了。后来补了一下,有一个非常巧妙的做法。

题解

T1

我们会发现它们之间连边的这个情况我们非常熟悉,我们可以对每一个 \(a_i\)\(i\),这样子他们所有的就可以变成:所有 \(a_i\) 相同的连边。在计算答案时,给它加回去就可以了。

而计算答案的时候我们可以贪心。每次选最大的和第二大的。如果说他总个数为奇数,即弃掉最小的,否则就全部加上去即可。

代码

#include<bits/stdc++.h>
#define inf 0x3f3f3f3f3f3f3f3f
#define int long long
#define endl '\n'
using namespace std;
const long long maxn=2e5+5;
int a[maxn];
map<int,vector<int> > mp;
signed main()
{
	freopen("matching.in","r",stdin);
	freopen("matching.out","w",stdout);
	ios::sync_with_stdio(0);
	cin.tie(0); cout.tie(0);
	int t;cin>>t;
	while(t--)
	{
		mp.clear();
		int n,ans=0;cin>>n;
		for(int i=1;i<=n;i++) cin>>a[i],a[i]-=i,mp[a[i]].push_back(a[i]+i);
		for(auto [x,v]:mp)
		{
			sort(v.begin(),v.end());
			int sum=0;
			for(int i=v.size()-1;i>=1;i-=2) if(v[i]+v[i-1]>0) sum+=v[i]+v[i-1];
			ans+=sum;
		}
		cout<<ans<<endl;
	}
	return 0;
}

T2

我们很容易就会想到贪心。但是具体又应该怎么贪呢?

我们可以把每一个工程的要求的人给他拆开。对于一个条件如果说满足条件,我就不管他。如果说不满足条件,我就可以把它放到一个优先队列里面。

我每次优先队列去看最小的那一个,如果说满足条件,那么此时当前这个工程的其中一个条件就满足了,把它删掉。如果说这个工程的条件全部满足了,我们就把它所带来的收益加上去。

此时实验法度我们会发现是就是每一个条件的和乘上一个 \(\log\)。是过得了的。

代码

#include<bits/stdc++.h>
#define int long long
using namespace std;
const int maxn=3e5+5;
int cnt=0;
vector<int> c[maxn],d[maxn],e[maxn],f[maxn];
int chao[maxn];
map<int,int> mp;
int a[maxn];
priority_queue<pair<int,int>,vector<pair<int,int> >,greater<pair<int,int> > > q[maxn];
void hh(int &x,int &y)
{
	if(mp.find(x)==mp.end()) mp[x]=++cnt;
	x=mp[x];
}
signed main()
{
	freopen("company.in","r",stdin);
	freopen("company.out","w",stdout);
	ios::sync_with_stdio(0);
	cin.tie(0); cout.tie(0);
	int n,ans=0;cin>>n;
	for(int i=1;i<=n;i++)
	{
		int x,y;cin>>x>>y;
		hh(x,y);
		a[x]=y;
	}
	int m;cin>>m;
	for(int i=1;i<=m;i++)
	{
		int k;cin>>k;
		for(int j=1;j<=k;j++)
		{
			int x,y;cin>>x>>y;
			hh(x,y);
			c[i].push_back(x);
			d[i].push_back(y);
			if(d[i][j-1]>a[c[i][j-1]]) q[c[i][j-1]].push({d[i][j-1],i}),chao[i]++;
		}
		int t;cin>>t;
		for(int j=1;j<=t;j++)
		{
			int x,y;cin>>x>>y;
			hh(x,y);
			e[i].push_back(x);
			f[i].push_back(y);
			if(chao[i]==0) a[x]+=y;
		}
		if(chao[i]==0) ans++;
	}
	while(1)
	{
		int flag=1;
		for(int i=1;i<=cnt;i++)
		{
			int flg=1;
			while(!q[i].empty()&&flg)
			{
				flg=0;
				if(a[i]>=q[i].top().first)
				{
					chao[q[i].top().second]--;
					if(chao[q[i].top().second]==0)
					{
						for(int j=0;j<e[q[i].top().second].size();j++)
							a[e[q[i].top().second][j]]+=f[q[i].top().second][j];
						flg=0;
						ans++;
					}
					q[i].pop();
					flg=1;
				}
			}
		}
		if(flag) break;
	}
	cout<<ans;
	return 0;
}

T3

T我们考虑按位拆分。对于V我们去枚举它的每一位。如果当前这一位为一则什么都不管,直接把它加到一个sum里面就可以了。如果说这一位为零,那么此时我们就去判断,如果说当前有边满足 \(w_i\and sum+2^{k}==sum+2^{k}\)的话(其中 \(k\) 为当前枚举到的位置)。
那么我们就把这条边连上,最后看一下我们的答案是否。被连成了一个连通块就可以了。

连通快我们用并查集做。

注意:要从大到小枚举位

代码

#include<bits/stdc++.h>
#define inf 0x3f3f3f3f3f3f3f3f
#define int long long
#define endl '\n'
using namespace std;
const long long maxn=5e5+5;
struct node1
{
	int u,v,w;
}a[maxn];
struct node2
{
	int x,y,ans;
}b[maxn];
int fa[maxn],n,m,q,v;
int get(int x)
{
	return fa[x]==x?x:fa[x]=get(fa[x]);
}
void merge(int u,int v)
{
	u=get(u),v=get(v);
	if(u!=v) fa[v]=u;
}
void check(int x)
{
	for(int i=1;i<=n;i++) fa[i]=i;
	for(int i=1;i<=m;i++) if((a[i].w&x)==x) merge(a[i].u,a[i].v);
	for(int i=1;i<=q;i++) if(get(b[i].x)==get(b[i].y)) b[i].ans=1;
}
signed main()
{
	freopen("path.in","r",stdin);
	freopen("path.out","w",stdout);
	ios::sync_with_stdio(0);
	cin.tie(0); cout.tie(0);
	cin>>n>>m>>q>>v;
	for(int i=1;i<=m;i++) cin>>a[i].u>>a[i].v>>a[i].w;
	for(int i=1;i<=q;i++) cin>>b[i].x>>b[i].y;
	check(v);
	int now=0;
	for(int i=60;i>=0;i--)
	{
		now^=(1ll<<i);
		if(!((v>>i)&1)) check(now),now^=(1ll<<i);
	}
	for(int i=1;i<=q;i++) cout<<(b[i].ans==1?"Yes":"No")<<endl;
	return 0;
}
posted @ 2025-07-31 18:28  Engle_Chen  阅读(19)  评论(0)    收藏  举报