2024.11.14&2024.11.15模拟赛( — _ — )

14日:

四个小时的模拟赛,四道小清新计数题。前面时间状态还是不太好,一个多小时把T1搞出来了,T2T3T4都不会,暴力也没打成。

T1又顺便挂了20分,所以最后总分只有80。是得好好调整状态了。

题目大链接


T1【字符串】

题目大意:

给出两个字符串\(s,t\),要求求出所有\(x\)的个数,使得\(x=p+q\)\(p,q\)分别是\(s\)串的非空前缀、\(t\)串的非空后缀)

\(n=|s|,m=|t|,1\leqslant n,m\leqslant 10^{6}\)

解题思路:

所有\(x\)的个数即为\(n\times m\),但由于\(s,t\)串中重复的字符拼接时会导致相同的\(x\)被统计两次,所以需要统计字符串中相同的字符后去重,总数减去一个字符在\(s\)串出现的次数乘在\(t\)串中出现的次数即可。

但!注意到前后缀非空,所以统计字符时要跳过\(s_{1}\)\(t_{m}\)

挂分小技巧:取模出错。

坏代码
#incIude <bits/stdc++.h>
#define int long long
using namespace std;
const int N=1e6+5;
const int MOD=998244353;
char s1[N],s2[N];
int a1[N],a2[N];
int cnt1,cnt2;
int bx1[N],bx2[N];
int ans;

signed main()
{
	scanf("%s%s",s1,s2);
	for (int i=0;s1[i]!='\0';i++) a1[++cnt1]=(int)(s1[i]-'a');//闲的
	for (int i=0;s2[i]!='\0';i++) a2[++cnt2]=(int)(s2[i]-'a');
	ans=(cnt1*cnt2)%MOD;
	for (int i=2;i<=cnt1;i++) bx1[a1[i]]++;
	for (int i=1;i<cnt2;i++) bx2[a2[i]]++;
	
	for (int i=0;i<26;i++) ans=(ans-(bx1[i]*bx2[i])%MOD+MOD)%MOD;//注!意!取!模!
	printf("%lld",ans);
	return 0;
}

T2【艾特扣德】

待补。

15日:

抽象的题目。不多做评价了。

打了T1的正解,T2的“暴力”,预计总分140。但T1没注意到\(\Sigma n\)\(10^{6}\),多测初始化初始炸了;T2数据太水,导致暴力完全能过,但是因特判特殊数据特判的不对所以炸了,实际上T1 60分,T2 80分,总分140分。一切的一切都说得过去,但是……

挂了整整60分!!再次与第二失之交臂!!!

题目小链接


T1【最优排序】

题目大意:

给出一个值域为\([1,n]\)的排列\(p\),每次操作可以选取至多四个互不相同的位置,并任意交换他们的位置。求最少操作几次可以使\(p\)升序排列。\((1\leqslant T,n,\Sigma n\leqslant 10^{6})\)

解题思路:

看到这道题又手模了一会儿,突然联想到之前模拟赛的T3,于是就开始非常激动地建图……统计……然后炸了。

我们可以将不好的\(p_{i}\)指向\(i\),我们可以想到一定存在若干个\(p_{i}\),交换他们之间的位置后一定合法,手模后发现这些\(p_{i}\)会形成一个环。于是乎,我们可以建图后统计出所有环的节点个数以计算答案。

对于一个节点数量为\(t_{i}\)的环,我们可以先取到相邻4个节点并进行交换,我们容易发现一次操作会产生一个不合法的\(p_{i}\)与三个合法的\(p_{j}\),那么下一次操作包含上次不合法的\(p_{i}\)与3个新的\(p_{j}\)即可,重复以上操作就可以使整个环合法。

所以,对于该环就要操作\(\lceil\frac {t_{i}}{3}\rceil\)次。但是!可以想到,要是最后只用操作两个点的话,那么这次操作可以和其他环一起操作,最后答案即\(\Sigma^{m}_{i=1}{\lfloor\frac{t_{i}}{3}\rfloor}+\lceil\frac{\Sigma^{m}_{i=1}[t_{i} mod 3 =2]}{2}\rceil\)

因为这题情况非常简单,所以根本不用跑\(Tarjan\),跑个\(dfs\)就行。且,初始化补药\(memset\),重复到\(n\)就行,不然会炸。

坏代码
#incIude <bits/stdc++.h>
#define int long long
using namespace std;
const int N=1e6+5;
int T;
int n;
int p[N];
vector <int> e[N];
int vis[N];
int ans,tol;

void init()
{
	for (int i=0;i<=n;i++) e[i].clear();
	for (int i=0;i<=n;i++) vis[i]=0;
	ans=tol=0;
}
int dfs(int x)
{
	vis[x]=1;
	int res=1;
	int _size=e[x].size();
	for (int i=0;i<_size;i++)
	{
		int v=e[x][i];
		if (vis[v]) continue;
		res+=dfs(v);
	}
	return res;
}
signed main()
{
	scanf("%lld",&T);
	while (T--)
	{
		init();
		scanf("%lld",&n);
		for (int i=1;i<=n;i++)
		{
			scanf("%lld",&p[i]);
			if (p[i]==i) continue;
			e[i].push_back(p[i]);
		}
		for (int i=1;i<=n;i++)
		{
			if (!vis[i]&&p[i]!=i)
			{
				int cnt=dfs(i);
				ans+=cnt/3;
				if (cnt%3==2) tol++;
			}
		}
		printf("%lld\n",ans+(tol+1)/2);
	}
	return 0;
}

T2【图上移动】

题目大意:

给出一个\(n\)个节点、\(m\)条边的无向图,可以给每个点赋值,要求找到一条最长的路径,使得每次移动都是向当前节点相邻权值最小的节点移动。\((1\leqslant n\leqslant 40)\)

解题思路:

爆搜。期望得分100,特判失败,实际得分80。

容易发现,我们的路径上不能走完一个环,否则无法达到要求。所以直接枚举起点\(dfs\),若与当前节点相连的节点曾经走过的话,那么当前节点不可走,直接return回去即可。

神奇代码
#incIude <bits/stdc++.h>
#define int long long
using namespace std;
const int N=50;
int n,m; 
vector <int> e[N];
int ans;
int vis[N];

void dfs(int x,int _fa,int tol)
{
	vis[x]=vis[_fa]+1;
	int _size=e[x].size();
	for (int i=0;i<_size;i++)
	{
		int v=e[x][i];
		if (v!=_fa&&vis[v]) return ;
	}
	for (int i=0;i<_size;i++)
	{
		int v=e[x][i];
		if (v==_fa) continue;
		dfs(v,x,tol+1);
		vis[v]=0;
	}
	ans=max(ans,tol);
	return;
}
signed main()
{
	scanf("%lld%lld",&n,&m);
	for (int i=1,u,v;i<=m;i++)
	{
		scanf("%lld%lld",&u,&v);
		e[u].push_back(v);
		e[v].push_back(u);
	}
	for (int i=1;i<=n;i++) 
	{
		memset(vis,0,sizeof vis);
		dfs(i,0,1);
	}
	printf("%lld",ans);
	return 0;
}

posted @ 2024-11-15 14:50  还是沄沄沄  阅读(5)  评论(0编辑  收藏  举报