加载中...

浙江理工大学2023acm队淘汰赛

浙江理工大学2023淘汰赛部分题目的理解

这里仅提供代码及思路,网站链接如下:
链接>http://47.96.116.66/contest.php?cid=5372<
难度梯度:A B C L D E F K I G H——/) /)
有错误的话请联系我,我也想知道 ฅ(• - •)ฅ

痛!请勿在比赛中使用endl(虽然我不会)

       .__                         ________  ____________  
  _____|  |__ _____    ____ ___.__.\_____  \/_   \_____  \ 
 /  ___/  |  \\__  \  /    <   |  | /  ____/ |   |/  ____/ 
 \___ \|   Y  \/ __ \|   |  \___  |/       \ |   /       \ 
/____  >___|  (____  /___|  / ____|\_______ \|___\_______ \
     \/     \/     \/     \/\/             \/            \/

问题 A: ACMer猫猫

题面:

你说的对,但是ICPC是由ACM自主研发的一款全新开放世界冒险游戏。游戏发生在一个被称作PTA的幻想世界,在这里,你将扮演一位名为ACMer的神秘角色,在自由的旅行中邂逅性格各异、能力独特的同伴们, 和他们一起击败强敌,找回失散的亲人——同时,逐步发掘PTA的真相。
刚刚成为成为ACMer的猫猫获得了一个由 A, C, M三个字符组成的字符串,它想知道有多少个长度为3的 ACM子串。

思想:枚举

代码:

点击查看代码
#include <bits/stdc++.h>
#include <iostream>
#include <cstring>
#include <algorithm>
#include <unordered_map>
#include <queue>
#pragma GCC optimize(2)
typedef long long LL;
using namespace std;
const int N = 1e5 + 10,maxn = 1e6 + 10;
char a[maxn];
int main()
{
	ios::sync_with_stdio(false);
	cin.tie(0);
	cout.tie(0);
	int n;
	cin >> n;
	int res=0;
	for(int i=1;i<=n;i++)
	{
		cin>>a[i];
		if(i>=3&&a[i-2]=='A'&&a[i-1]=='C'&&a[i]=='M')
		{
			res++;
		}
	}
	cout<<res;
}

问题 B: 猫猫本

题面:

猫猫在《猫猫本》上写了n个由2个小写字母组成的字符串。现在猫猫想知道每写一个新的字符串后,《猫猫本》上有多少字符串和新添加的字符串是相似的。猫猫认为,当且仅当只有一个位置相同两个字符串是相似的。例如aa和ab,ab和cb是相似的;但是aa和aa,aa和cc是不相似的。

思想:容斥\哈希\map

点击查看代码
#include <bits/stdc++.h>
#include <iostream>
#include <cstring>
#include <algorithm>
#include <unordered_map>
#include <queue>
#pragma GCC optimize(2)
typedef long long LL;
using namespace std;
const int N = 1e5 + 10,maxn = 1e6 + 10;
int a[N],b[N];
int main()
{
	ios::sync_with_stdio(false);
	cin.tie(0);
	cout.tie(0);
	int n;
	cin >> n;
	for(int i = 1;i <= n; i++){
		string s;
		cin>>s;
		int t1=s[0]-'a'+1;
		int t2=s[1]-'a'+1;
		cout<<a[t1]+b[t2]-2*b[t1*131+t2]<<"\n";
		a[t1]++;
		b[t2]++;
		b[t1*131+t2]++;
	}
	return 0;
}

问题 C: 猫猫game

题面:

猫猫一个人在实验室玩游戏。
游戏规则是这样的:每轮游戏会给猫猫两个整数 n,m,猫猫在区间 [1,n] 内,随机取m个整数。该操作进行\(2023^{2023}\)次。如果每次这m个数中,一定存在一个整数是另一个整数的倍数,则猫猫回答Yes,否则回答No。请你告诉猫猫每轮游戏的结果。

思想:鸽巢原理,真的不是找质数(赛时我在干这个蠢事)

样本大至正无穷,相当于取遍样本。下面解释为什么找质数是错误的,注意到《每个》,若n=10,取56789;这时候质数是2357,显然前面的数字更多更符合题意。其次,当n比较大时,一个数的质数个数是\(\frac{X}{lnX}\)级别的,而一个数的一半是\(\frac{X}{2}\)。显然前者小于后者。
下面证正确思想:m>(n+1)/2。每次取一半的时候都是满足题意的,n为偶数时是不影响的,n为奇数时变成n+1(自己理解下吧)

代码:

点击查看代码
#include <bits/stdc++.h>
#include <iostream>
#include <cstring>
#include <algorithm>
#include <unordered_map>
#include <queue>
#pragma GCC optimize(2)
typedef long long LL;
using namespace std;
const int N = 1e5 + 10,maxn = 1e6 + 10;
void solve(){
	int n,m;
	cin>>n>>m;
	if(m>(n+1)/2){
		puts("Yes");
	} else {
		puts("No");
	}
}
int main()
{
	ios::sync_with_stdio(false);
	cin.tie(0);
	cout.tie(0);
	int t;
	cin >> t;
	while( t-- ){
		solve();
	}
	return 0;
}

问题 D: 猫猫mex

题面:

猫猫一个人在实验室玩游戏。游戏规则是这样的:每轮游戏会给猫猫两个整数\(n,m\),猫猫在区间\([1,n]\)内,随机取m个整数。该操作进行\(2023^{2023}\)次,如果每次这m个数中,一定存在一个整数是另一个整数的倍数,则猫猫回答\(Yes\),否则回答\(No\)。请你告诉猫猫每轮游戏的结果。

输入格式:

第一行一个整数T(\(1\le T\le10^4\)) 表示测试数据个数。
接下来\(T\)行,每行2个整数\(n,m\)(\(4\le n\le 10^5\),\(2\le m\le n\))。

思想:思维,set/线段树/分块

C++中set是有序的,不妨我们设置一个全局U,涵盖所有的数。
添加一个数=在U中删除一个数
删除=添加
这样我们的begin()一定是所求。

代码:

点击查看代码
#include <bits/stdc++.h>
#include <iostream>
#include <cstring>
#include <algorithm>
#include <unordered_map>
#include <queue>
#pragma GCC optimize(2)
typedef long long LL;
using namespace std;
const int N = 1e5 + 10,maxn = 1e6 + 10;
set<int>s;
int main()
{
	ios::sync_with_stdio(false);
	cin.tie(0);
	cout.tie(0);
	int q,op,x;
	cin >> q;
	for(int i=0;i<N;i++){
		s.insert(i);
	}
	for(int i=1;i<=q;i++){
		cin>>op;
		if(op==1){
			cin>>x;
			s.erase(x);
		}
		if(op==2){
			cin>>x;
			s.insert(x);
		}
		if(op==3){
			cout<<*s.begin()<<"\n";
		}
	}
	return 0;
}

问题 E: 猫猫sort

题面:

猫猫有一个升序数组,数组中元素两两不同。但是邪恶的paopaooux把他的数组打乱了,猫猫每次可以选择两个元素并交换他们在数组中的位置。
现在猫猫想知道自己最少多少次操作可以重新使数组升序。

思想: 离散化、贪心/置换环

解释已经放代码

代码:

点击查看代码
#include <bits/stdc++.h>
#include <iostream>
#include <cstring>
#include <algorithm>
#include <unordered_map>
#include <queue>
#pragma GCC optimize(2)
typedef long long LL;
using namespace std;
const int N = 2e5 + 10,maxn = 1e6 + 10;
map<LL,int> mp;
LL num[N],a[N],ans; 
int main()
{
	ios::sync_with_stdio(false);
	cin.tie(0);
	cout.tie(0);
	LL n;
	cin>>n;
	for(int i=1;i<=n;i++){
		cin>>num[i];
		mp[num[i]]=i;
		a[i]=num[i];//a[i]为值,mp[a[i]]为下标
	}
	sort(a+1,a+n+1);//仅对值排序
	// 1 -1 -4 5 4
	//-4 -1  1 4 5  排序后 
	for(int i=1;i<=n;i++)
	{
		if(num[i] == a[i]) continue;//不需要
		LL x=mp[a[i]];//置换,找出a[i]的原位置,例如i=1,a[i]=-4,x=3,y=-4; 
		swap(num[x],num[i]);//将原数组下标x,i置换
		mp[a[i]]=i;
		mp[num[x]]=x;//原数组下标置换
		ans++;
	}
	cout<<ans;
	return 0;
}

问题 F: 猫猫旅行

题面:

猫猫生活的国家有n个城市,城市从 1~n 编号,城市之间通过公路连接。这个国家有一个奇怪的规定:进入城市需要持有k天内办理的通行证,并且国家的每个城市都可以重新办理新的通行证。现在,猫猫有q个旅行计划,每个旅行计划从u城市出发,到达v城市。请你告诉猫猫每个旅行计划是否可行。
保证整个国家连通,且不存在重复公路。

输入:

第一行三个整数n,m,k,q (2≤n≤2×105,n-1≤m ≤ min(n(n-1)/2,2×105),1≤k≤109,1≤q≤2×105),分别表示表示城市数,公路数,通行证的有效期以及旅行计划个数。
接下来m行,每行三个整数u,v,w(1≤u,v≤n且u≠v,1≤w≤109),表示城市u,v之间有一条公路连接,猫猫通过这条公路需要花费w天。
接下来q行,每行两个整数u,v(1≤ u,v≤n),表示猫猫每个旅行计划的起点与终点。

输出:

q行,如果旅行计划可行,输出Yes;否则输出No。

思想:并查集(不是最短路)

一个简单的并查集,可是赛时的蒟蒻我并没看见。

代码:

点击查看代码
#include <bits/stdc++.h>
#include <iostream>
#include <cstring>
#include <algorithm>
#include <unordered_map>
#include <queue>
#pragma GCC optimize(2)
typedef long long LL;
using namespace std;
const int N = 2e5 + 10,maxn = 1e6 + 10;
int p[N];
int find(int x)
{
	if(p[x]!=x)
	{
		p[x]=find(p[x]);
	}
	return p[x];
}
int main()
{
	ios::sync_with_stdio(false);
	cin.tie(0);
	cout.tie(0);
	int m,n,k,q;
	cin>>n>>m>>k>>q;
	for(int i=1;i<=n;i++)
	{
		p[i]=i;
	}
	for(int i=1;i<=m;i++)
	{
		int u,v,w;
		cin>>u>>v>>w;
		if(w>k){
			continue;
		}
		u=find(u);
		v=find(v);
		if(u!=v){
			p[u]=v;
		}
	}
	for(int i=1;i<=q;i++)
	{
		int u,v;
		cin>>u>>v;
		if(find(u)==find(v))
		{
			cout<<"Yes\n";
		}
		else
		{
			cout<<"No\n";
		}
	}
	return 0;
}

问题 G: 猫猫卡牌

题面:

猫猫与小石头在实验室玩比大小的游戏。
实验室有n张卡牌,编号为1~n。游戏有两种规则:
规则一:先手的玩家从牌堆中抽出一张,后手的玩家从牌堆中抽出一张,然后双方把牌放回牌堆。
规则二:先手的玩家从牌堆中抽出一张后立刻放回牌堆,然后后手的玩家也从牌堆中抽出一张后立刻放回。
无论哪种规则,编号大的一方获胜。如果编号相同,则该轮游戏平局。游戏共进行m轮。在游戏开始前,他们会通过抛硬币的方式决定接下来的m轮游戏谁先手。如果硬币为正面,则猫猫先手;为反面则小石头先手。现在猫猫想知道,在规则一或规则二的情况下,自己至少赢k把的概率。
猫猫不喜欢浮点数,因此它想知道这两个答案对 1000000007 取模的结果:即设答案化为最简分式后的形式为a/b (其中gcd(a,b)=1),输出整数 x 使得 bx≡a (mod 1000000007) 且 0≤ x<1000000007(可以证明这样的整数 x 是唯一的)。

输入

第一行三个整数n,m,k(2≤n≤109,1≤ m≤106,0≤ k≤m),分别表示卡牌的数量,游戏的轮数和猫猫至少赢的轮数。
第二行一个整数x(0≤x≤1),如果x为1,表示硬币为正面;如果x为0,表示硬币为反面。

输出

两行,第一行输出在规则一的情况下猫猫至少赢k把的概率;第二行输出在规则二的情况下猫猫至少赢k把的概率。

思想:数学,概率,逆元

1.首先一眼逆元(计qmi(x,mod-2,mod)为x的逆元),a/b=x,所以a*(b的逆元)为所求
2.规则1:不难发现就是在牌堆中任选两张(不可能重合),一半一半,猫猫赢一把概率是\(\frac{1}{2}\)
3.规则2:转化为规则1,不过多了重合的情况。所有样本是\(\frac{1}{n}*\frac{1}{n}\),为了不重合,在n中任选2张不同即可满足(相当于2的牌映射到1上),为\(C(n,2)\),由于算一个人的概率,即除2,变成\(\frac{\frac{C(n,2)}{2}}{n^2}=\frac{n-1}{2n}\)
4.至少赢k把的概率为赢i(i>=k)的概率相加(题目符合二项分布,高中公式)

代码:

点击查看代码
#include <bits/stdc++.h>
#include <iostream>
#include <cstring>
#include <algorithm>
#include <unordered_map>
#include <queue>
#pragma GCC optimize(2)
typedef long long LL;
using namespace std;
const  LL N = 5e6 + 10,maxn = 1e6 + 10,mod = 1e9 + 7;
LL fact[N], infact[N];
LL qmi(LL a,LL b)
{
	LL res=1;
	while(b)
	{
		if(b&1) res=res*a%mod;
		a=a*a%mod;	
		b>>=1;
	}
	return res;
}
void init()
{
	fact[0]=infact[0]=1;
	for(LL i=1;i<maxn;i++)
	{
		fact[i]=fact[i-1]*i%mod;
		infact[i]=infact[i-1]*qmi(i,mod-2)%mod;
	}
}
LL C(LL a,LL b)
{
	return fact[a] * infact[b] % mod * infact[a-b] % mod;
}
int main()
{
	ios::sync_with_stdio(false);
	cin.tie(0);
	cout.tie(0);
	init();
	LL x,m,n,k;
	cin>>n>>m>>k>>x;
	LL p1=qmi(2,mod-2),p2=(n-1)*qmi(2*n%mod,mod-2)%mod;
	LL res1=0,res2=0;
	for(LL i=k;i<=m;i++)
	{
		res1=(res1+qmi(p1,i)*qmi(1+mod-p1,m-i)%mod *C(m,i)%mod)%mod;
		res2=(res2+qmi(p2,i)*qmi(1+mod-p2,m-i)%mod *C(m,i)%mod) % mod;
	}
	cout<<res1<<"\n"<<res2<<"\n";
	
	return 0;
}

问题 I: 猫猫区间

题面:

猫猫有一个整数x,与一个正整数区间[L,R]。现在猫猫想把这个区间的整数染色,但是猫猫不喜欢与x互质的数,所以这些数不会被染色。请你告诉猫猫可以染色的整数有多少个。

输入:

一行,三个整数L,R,x(1≤L≤R≤10^(18),1≤x≤1012)。

输出:

输出一个整数,表示可以染色的整数个数。

思想:容斥

1.类似于前缀和:[L,R]中被x互质的数同样是[1,R]-[1,L-1]中的数
2.类似于x=6,质因数为2,3.所以在遍历[1,N]的时候2,3的倍数都不会与x互质,但是2*3=6会算上两次,这时候需要减去。
那如果质因数很多时,我们该如何取选取呢?
——————————————用二进制枚举每个数。

代码:

点击查看代码
#include <bits/stdc++.h>
#include <iostream>
#include <cstring>
#include <algorithm>
#include <unordered_map>
#include <queue>
#pragma GCC optimize(2)
typedef long long LL;
using namespace std;
const  LL N = 1e5 + 10,maxn = 2e6 + 10,mod = 1e9 + 7;
LL js(LL n,LL x)
{
	vector<LL> v;
	for(LL i=2;i<=x/i;i++)
	{
		if(x%i==0)
		{
			v.push_back(i);
			while(x%i==0)
			{
				x/=i;
			}
		}
	}
	if(x>1) v.push_back(x);
	LL res=0;
	for(LL i=1;i<(1 << v.size() );i++)
	{
		LL cur=1;
		LL cnt=0;
		for(LL j=0;j<v.size();j++)
		{
			if(i >> j & 1)
			{
				cur*=v[j];
				cnt++;
			}
		}
		if(cnt&1)
		{
			res+=n/cur;
		}
		else
		{
			res-=n/cur;
		}
	}
	return res;
}
int main()
{
	ios::sync_with_stdio(false);
	cin.tie(0);
	cout.tie(0);
	LL l,r,x;
	cin>>l>>r>>x;
	LL res=js(r,x)-js(l-1,x);
	cout<<res<<"\n";
	return 0;
}

问题 K: 猫猫与甜品

题面:

猫猫在实验室吃甜品喽!
猫猫有n个甜品,每个甜品可以给猫猫带来a的快乐值与b的健康值。初始猫猫快乐值和健康值都为0,猫猫不会让自己的健康值为负数,猫猫想知道自己以合理的顺序吃这些甜品(可以不吃完),猫猫的最大快乐值是多少。

输入:第一行一个整数n(1≤n≤500),表示甜品的数量。

接下来n行,每行2个整数ai,bi(1≤i≤n,-100≤ ai,bi≤100),表示第i个甜品的快乐值与健康值。

输出:

输出一个整数,表示猫猫的最大快乐值。

思想:01背包

代码:

点击查看代码

问题 L: 猫猫序

题面;

我们都知道
树的dfs序是一棵树从根节点出发,dfs遍历时依次经过的节点序列。
现在猫猫有一棵包含n个结点的有根树,结点从1~n编号,1号点为根节点。猫猫想让你告诉它,这棵树的dfs序有多少种。
由于答案很大,你需要对998244353取模。

输入:

我们都知道
树的dfs序是一棵树从根节点出发,dfs遍历时依次经过的节点序列。
现在猫猫有一棵包含n个结点的有根树,结点从1~n编号,1号点为根节点。猫猫想让你告诉它,这棵树的dfs序有多少种。
由于答案很大,你需要对998244353取模。

输出:

输出一个整数,表示这棵树dfs序的个数。

思想:也许是树形dp

1.例如节点1, 2、4为1的子节点,1的dfs序是由2,4构成,2,4分别独立,计算1时需要2*4(应该看的懂吧,我讲的比较抽象)

代码:翁教

点击查看代码
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const ll N = 2e5 + 10, mod = 998244353;
vector<ll>g[N];
ll fact[N], f[N];
void init() {
	fact[0] = 1;
	for (ll i = 1; i < N; i++) {
		fact[i] = fact[i - 1] * i % mod;
	}
}
void dfs(ll u, ll fa) {
	f[u] = 1;//记得初始化
	for (auto v : g[u]) 
	{
		if (v == fa) 
		{
			continue;
		}
		dfs(v, u);//遍历子节点的子节点
		f[u] = f[u] * f[v] % mod;
	}
	f[u] = f[u] * fact[g[u].size() - 1] % mod;//这里因为时子节点的个数,需要-1(这个父节点)
}
int main() {
	// freopen("8.in", "r", stdin);
	// freopen("8.out", "w", stdout);
	init();
	ll n;
	scanf("%lld", &n);
	for (ll i = 1, u, v; i < n; i++) {
		scanf("%lld%lld", &u, &v);
		g[u].push_back(v);
		g[v].push_back(u);
	}
	g[1].push_back(-1);
	dfs(1, -1);
	printf("%lld\n", f[1]);
}

问题 M: 猫猫的位运算

题面:

猫猫有a,b两个整数,想计算出 a&b,a|b,a⊕b 的值,猫猫很快就算出a&b和a|b的值,还没来及的验证是否正确。但是猫猫太困了,很快它就睡着了,醒来后发现原来的a,b两个整数被邪恶的paopaooux擦除掉了。你可以告诉猫猫a⊕b的值或者告诉猫猫算错了。

输入:

第一行一个整数T(1≤T≤105) ,表示测试数据个数。
接下来T行,每行两个整数x,y(1≤ x,y≤109),分别表示a&b,a|b的值。

输出:

T行,每行一个整数,表示a⊕b的值。若a⊕b不存在则输出-1。

思想:


tips:<&> 有0出0,全1出1 ------------ <|> 有1出1,全0出0
其实自己可以推的:
对于a&b,a|b,用二进制表示,例如2,5分别为10,101
这时候发生了矛盾:因为10中的“1”表示ab二进制第二位全是1(与为1),同时101中“0”表示ab二进制第二位全为0(或为0)

代码

点击查看代码
#include <iostream>
#include <cstring>
#include <algorithm>
#include <unordered_map>
#include <queue>
#pragma GCC optimize(2)
typedef long long LL;
using namespace std;
const int N = 1e5 + 10,maxn = 1e6 + 10;
void solve() 
{
	int x, y;
	cin>>x>>y;
	if (y-x!=(x^y))
	{
		cout<<-1<<"\n";
	}
	else
	{
		int h=x^y;
		cout<<h<<"\n";
	}
}
int main() 
{
	ios::sync_with_stdio(false);
	cin.tie(0);
	cout.tie(0);
	int t;
	cin>>t;
	while (t--)
	{
		solve();
	}
	return 0;
}
posted @ 2023-02-25 21:52  shany212  阅读(319)  评论(1编辑  收藏  举报