梦熊联盟2025 -- 云智计划 -- 1 月 27 日 --【CSP-S】模拟赛 #7 div2 T1~T3 题解 附T4的50分法

T1:科技树(TechTree)

题目大意

给你一棵树,每一次你可以点一个没点过的点,问你任意一个点最早什么时候点?最晚什么时候点?

解法

题目都已经说了,是树,那么最早就是它前面所有的前置科技都已经点完之后再点它,即 \(dfn_x\)。最晚的即是以它为根的这棵子树不点,其他的都点。因为以他为根的这棵子树里面必须要点他,而其他的点对他没有任何要求,即 \(n-siz_x\)

代码:

#include<bits/stdc++.h>
#define int long long
using namespace std;
const int maxn=1e5+5;
vector<int> g[maxn];
int dep[maxn],siz[maxn];
void dfs(int x,int fa)
{
	siz[x]=1,dep[x]=dep[fa]+1;
	for(auto &to:g[x])
	{
		if(to==fa) continue;
		dfs(to,x);
		siz[x]+=siz[to];
	}
}
signed main()
{
	freopen("TechTree.in","r",stdin);
	freopen("TechTree.out","w",stdout);
	ios::sync_with_stdio(0);
	cin.tie(0); cout.tie(0);
	int t;cin>>t;
	while(t--)
	{
		int n;cin>>n;
		for(int i=1;i<n;i++)
		{
			int u,v;cin>>u>>v;
			g[u].push_back(v);
			g[v].push_back(u);
		}
		dfs(1,0);
		for(int i=1;i<=n;i++) cout<<dep[i]<<' '<<n-siz[i]+1<<'\n';
		for(int i=1;i<=n;i++) g[i].clear();
	}
	return 0;
}

T2:B. 增益选择(Buff)

题目大意

给你一个长度为数 \(n\) 的数组 \(a\),问你任意选两个点,这两个点之比积更大,有多少种方案?

解法

我们想一想有什么情况两个数之和比积更大?

  • 负数乘正数
  • 正数乘0
  • 正数乘1(含1)

所以我们只需要统统计正数,负数,0和1的个数即可。

代码:

#include<bits/stdc++.h>
#define int long long
using namespace std;
const int maxn=1e6+5;
signed main()
{
	freopen("Buff.in","r",stdin);
	freopen("Buff.out","w",stdout);
	ios::sync_with_stdio(0);
	cin.tie(0); cout.tie(0);
	int t;cin>>t;
	while(t--)
	{
		int n,cntz=0,cntf=0,cnt1=0,cnt0=0;cin>>n;
		for(int i=1;i<=n;i++)
		{
			int x;cin>>x;
			if(x>0) cntz++;
			if(x<0) cntf++;
			if(x==1) cnt1++;
			if(x==0) cnt0++;
		}
		int ans=0;
		ans+=cntz*cntf;//正数*负数
		ans+=cnt1*(cntz-cnt1);//正数*1(不含1)
		ans+=cnt1*(cnt1-1)/2;//1*1
		ans+=cnt0*cntz;//0*正数
		cout<<ans<<endl;
	}
	return 0;
}

T3:C. 树状数组(FenwickTree)

题目大意

给你一棵树状数组 \(c\) 的某位是否为0,请问至少操作多少次才能变成 \(c\)(具体数值自定,只需满足是否为0即可)?

解法

根据树状数组的原理,我们可以知道:

$ c_x=\sum^x_{i=x-lowbit(x)+1} a_i$

其中\(lowbit(x)\)\(x\)的因数中最大的2的幂次方。如 \(lowbit(12)=4\)

\(lowbit(x)=2^d\)\(c_x\) 可以进一步表示成:

\(c_x=a_x+\sum^{d-1}_{i=0} c_{x-2^i}\)

比如 \(c_{16}=a_{16}+c_{15}+c_{14}+c_{12}+c_{8}\)

至此不难发现,每一个位置只跟 \(a_c\)\(c_{x-2^i}\) 有关,且每个位置至多操作一次。可以得出以下结论:

  1. 如果 \(c_x\) 为0且 \(c_{x-2^i}\) 中仅有一个非零,那么 \(a_x\) 必须被操作一次。
  2. 如果 \(c_x\) 不为0且 \(c_{x-2^i}\) 中只有零,那么 \(a_x\) 必须被操作一次。

从前往后扫一遍,每个位置最多来一遍时间复杂度为 \(O(n)\)

代码:

#include<bits/stdc++.h>
#define int long long
using namespace std;
const int maxn=2e5+5;
int a[maxn];
signed main()
{
	freopen("FenwickTree.in","r",stdin);
	freopen("FenwickTree.out","w",stdout);
	ios::sync_with_stdio(0);
	cin.tie(0); cout.tie(0);
	int t;cin>>t;
	while(t--)
	{
		int n,ans=0;cin>>n;
		for(int i=1;i<=n;i++)
		{
			char c;cin>>c;
			a[i]=c-'0';
		}
		for(int i=1;i<=n;i++)
		{
			int cnt=0,d=i&(-i);
			for(int j=1;j<d;j*=2) cnt+=a[i-j];
			if(a[i]==0&&cnt==1) ans++;
			if(a[i]!=0&&cnt==0) ans++;
		}
		cout<<ans<<'\n';
	}
	return 0;
}

附:T4:D. 部队编号(Code) 50pts

题目大意

给你一个字符串 \(s\) 问里面有多少个“114514”串?

“114514”串 定义:其可以分成6段非空的子段,其中第一段、第二段和第五段是相同的,第三段和第六段是相同的;

50pts解法

不难发现,一共就只有3种子串,枚举三者的长度与起点,判断一下即可。

代码:

#include<bits/stdc++.h>
#define int long long
using namespace std;
const int maxn=15;
signed main()
{
	freopen("Code.in","r",stdin);
	freopen("Code.out","w",stdout);
	ios::sync_with_stdio(0);
	cin.tie(0); cout.tie(0);
	int t;cin>>t;
	while(t--)
	{
		string s;cin>>s;
		int n=s.size(),ans=0;
		s=' '+s;
		for(int i=1;i<=n;i++)
		{
			for(int j=1;j+i+j+i+i<=n;j++)
			{
				for(int k=1;i+j+k+i+j+i<=n;k++)
				{
					for(int l=1;l+i+j+k+i+j+i-1<=n;l++)
					{
						string s1=s.substr(l,i),s2=s.substr(i+l,i),s3=s.substr(i+i+l,j)//且由于其中有一个子串没有任何要求,所以就五个。
						,s5=s.substr(i+i+j+k+l,i),s6=s.substr(i+i+j+k+i+l,j);
						if(s1==s2&&s2==s5&&s3==s6) ans++;
					}
				}
			}
		}
		cout<<ans<<endl;
	}
	return 0;
}
posted @ 2025-02-03 12:25  Engle_Chen  阅读(60)  评论(0编辑  收藏  举报