2023第二届“清华社杯”大学生算法大赛

1.变化的举行矩形在这里插入图片描述

签到题,直接用cout会输出1e6之类的,用printf或者cout<<fixed<<setprecision(6)

2.吃鸡梦之队
在这里插入图片描述

枚举加二分查找

#include<iostream>
#include<cstring>
#include<set>
#include<unordered_map>
#include<unordered_set>
#include<vector>
using namespace std;
const int N=1e5+5;
int p1[N], p2[N];
int main() {
	int t;
	scanf("%d",&t);
	while (t--) {
		set<int> s1,s2, s3, s4;
		int n;
		scanf("%d",&n);
		for (int i = 0; i < n; i++) {
			scanf("%d",&p1[i]);
		}
		for (int i = 0; i < n; i++) {
			scanf("%d", &p2[i]);
		}
		for (int i = 0; i < n; i++) {
			if (p1[i] == 1) {
				s1.insert(p2[i]);
			}
			else if (p1[i] == 2) {
				s2.insert(p2[i]);
			}
			else if (p1[i] == 3) {
				s3.insert(p2[i]);
			}
			else {
				s4.insert(p2[i]);
			}
		}
		int ans=1e9;
		for (int i1 : s1) { //s1大s2小
			auto it = s2.upper_bound(i1);
			if (it == s2.begin()) {
				continue;
			}
			it--;
			int i2 = *it;
			//s3小s4大
			auto it4 = s4.lower_bound((i1+i2)/2);
			if (it4 != s4.end()) {
				int i4 = *it4;
				auto it3 = s3.upper_bound(i4);
				if (it3 != s3.begin()) {
					it3--;
					int i3 = *it3;
					ans=min(ans, max(i1 - i3, i4-i2));
				}
			}
			auto it3 = s3.upper_bound((i1 + i2) / 2);
			if (it3 != s3.begin()) {
				it3 -- ;
				int i3 = *it3;
				auto it4 = s4.lower_bound(i3);
				if (it4 != s4.end()) {
					int i4 = *it4;
					ans = min(ans, max(i1 - i3, i4 - i2));
				}
			}
			//s3大s4小
			it3 = s3.lower_bound((i1 + i2) / 2);
			if (it3 != s3.end()) {
				int i3 = *it3;
				auto it4 = s4.upper_bound(i3);
				if (it4 != s4.begin()) {
					it4--;
					int i4 = *it4;
					ans = min(ans, max(i1 - i4, i3 - i2));
				}
			}

			it4 = s4.upper_bound((i1 + i2) / 2);
			if (it4 != s4.begin()) {
				it4--;
				int i4 = *it4;
				auto it3 = s3.lower_bound(i4);
				if (it3 != s3.end()) {
					int i3 = *it3;
					ans = min(ans, max(i1 - i4, i3 - i2));
				}
			}

		}
		for (int i2 : s2) { //s1小s2大
			auto it = s1.upper_bound(i2);
			if (it == s1.begin()) {
				continue;
			}
			it--;
			int i1 = *it;
			//s3小s4大
			auto it4 = s4.lower_bound((i1 + i2) / 2);
			if (it4 != s4.end()) {
				int i4 = *it4;
				auto it3 = s3.upper_bound(i4);
				if (it3 != s3.begin()) {
					it3--;
					int i3 = *it3;
					ans = min(ans, max(i2 - i3, i4 - i1));
				}
			}
			auto it3 = s3.upper_bound((i1 + i2) / 2);
			if (it3 != s3.begin()) {
				it3--;
				int i3 = *it3;
				auto it4 = s4.lower_bound(i3);
				if (it4 != s4.end()) {
					int i4 = *it4;
					ans = min(ans, max(i2 - i3, i4 - i1));
				}
			}
			//s3大s4小
			it3 = s3.lower_bound((i1 + i2) / 2);
			if (it3 != s3.end()) {
				int i3 = *it3;
				auto it4 = s4.upper_bound(i3);
				if (it4 != s4.begin()) {
					it4--;
					int i4 = *it4;
					ans = min(ans, max(i2 - i4, i3 - i1));
				}
			}
			it4 = s4.upper_bound((i1 + i2) / 2);
			if (it4 != s4.begin()) {
				it4--;
				int i4 = *it4;
				auto it3 = s3.lower_bound(i4);
				if (it3 != s3.end()) {
					int i3 = *it3;
					ans = min(ans, max(i2 - i4, i3 - i1));
				}
			}
		}
		printf("%d\n", ans);
	}

}

3.洞穴探险
在这里插入图片描述

比赛没做出来,先进行并查集缩点(因为是无向图,有向图是targan),建立超级源节点,是环的节点值设置为1否则为0,再进行倍增,求每个路径值,查询过程先并查集判断是否处于同一图中,不属于同一图中直接输出,属于同一图中再求ab之间路径长度是否大于1(使用lca算法),大于1说明经过缩点存在多条路径。

4.火柴棒等式
在这里插入图片描述
在这里插入图片描述

枚举所有合法的等式并记录状态,每个数字可以用7位bit表示,所以等式可以用int表示

#include<iostream>
#include<cstring>
#include<set>
#include<unordered_map>
#include<unordered_set>
#include<vector>
using namespace std;
const int N=1e3+5;
unordered_map<int,vector<int>> eq;
//unordered_set<int> num[7];
int num[10];
int cnt[10];
unordered_set<int> num_no[7];
int main() {
	num_no[0].insert(1), num_no[0].insert(4);
	num_no[1].insert(5), num_no[1].insert(6);
	num_no[2].insert(2);
	num_no[3].insert(1), num_no[3].insert(4),num_no[3].insert(7);
	num_no[4].insert(1), num_no[4].insert(3),num_no[4].insert(4),num_no[4].insert(5),num_no[4].insert(7), num_no[4].insert(9); //134579
	num_no[5].insert(1), num_no[5].insert(2),num_no[5].insert(3), num_no[5].insert(7);//1237
	num_no[6].insert(0), num_no[6].insert(1), num_no[6].insert(7);//017
	for (int i = 0; i < 10; i++) {
		int s = 0;
		int ct=0;
		for (int j = 0; j < 7; j++) {
			if (!num_no[j].count(i)) {
				s |= 1<<j;
				ct++;
			}
		}
		cnt[i] = ct;
		num[i] = s;
	}
	for (int i = 0; i < 10; i++) {
		for (int j = 0; j < 2; j++) {//+-
			if(j==0){//-
				for (int k = 0; k <= i; k++) {
					int r = i - k;
					int sum = cnt[i] + 1 + cnt[k] + cnt[r];
					eq[sum].push_back((num[i]<<16)|(1<<14)|(num[k]<<7)|num[r]);
				}
			}
			else {//+
				for (int k = 0; k < 10 - i; k++) {
					int r = i + k;
					int sum = cnt[i] + 2 + cnt[k] + cnt[r];
					eq[sum].push_back((num[i] << 16) | (3 << 14) | (num[k] << 7) | num[r]);
				}
			}
		}
	}
	int t;
	scanf("%d",&t);
	while (t--) {
		getchar();
		char x,y,z,op;
		scanf("%c %c %c = %c",&x,&op,&y,&z);
		int x1 = x-'0', y1 = y-'0', z1 = z-'0';
		int sum = cnt[x1]+cnt[y1]+cnt[z1]+(op=='+'?2:1);
		int ans=1e9;
		int s1 = (num[x1]<<16)|(op == '+' ? 3<<14 : 1<<14)|(num[y1]<<7)|num[z1];
		if (eq[sum].empty()) {
			printf("-1\n");
		}
		else {
			for (int s : eq[sum]) {
				int ct=0;
				for (int i = 24; i >= 0; i--) {
					ct += (((s>>i)&1) != ((s1>>i)&1));
				}
				ans=min(ans, ct);
			}
			printf("%d\n",ans/2);
		}
	}

}

6.排列
在这里插入图片描述

#include<iostream>
#include<cstring>
#include<set>
using namespace std;
int main() {
	int t;
	scanf("%d",&t);
	while (t--) {
		long long n;
		scanf("%lld",&n);
		if (n == 1) {
			printf("0\n");
			continue;
		}
		if (!(n & 1)) {
			long long ans = (n / 2 + 1 + n) * (1 + n - (n / 2 + 1)) - (n / 2 + 1 )*(n / 2) - 1;
			printf("%lld\n", ans);
		}
		else {
			long long ans = (n/2+1+n)*(1+n-(n/2+1))-(n/2+1+n/2+2)-(1+n/2)*(n/2);
			printf("%lld\n", ans);
		}
	}
}

7.数组查询
在这里插入图片描述

二分

#include<iostream>
#include<cstring>
#include<set>
using namespace std;
const int N=1e5+5;
int arr[N];
int main() {
	int t;
	scanf("%d",&t);
	while (t--) {
		int n;
		scanf("%d",&n);

		for (int i = 0; i < n; i++) {
			scanf("%d",&arr[i]);
		}
		set<int> s;
		int op ;
		for (int i = 0; i < n; i++) {
			s.insert(arr[i]);
			scanf("%d",&op);
			auto it = s.upper_bound(op);
			if (it == s.begin()) {
				printf("%d\n", -1);
			}
			else {
				it--;
				printf("%d\n", * it);
			}
		}
	}
}

8.新冠病毒
在这里插入图片描述

#include<iostream>
using namespace std;
int main() {
	int n;
  cin>>n;
  cout<<(long long)n*(n+1)/2<<endl<<(n+1)/2<<endl;
}

9.有星星就表白

先判断是否是树,再求最大节点度数,最大节点度数小于等于2或者度数大于2的节点个数为1则表白
在这里插入图片描述

#include<iostream>
#include<cstring>
#include<set>
using namespace std;
const int N=1e5+5;
int d[N];
int cnt=1;
int h[N], e[N<<1], nx[N<<1];
void add(int a, int b) {
	e[cnt]  =b, nx[cnt] = h[a], h[a] = cnt++;
}
int main() {
	int t;
	scanf("%d",&t);
	while (t--) {
		memset(d,0,sizeof d);
		int n,m;
		scanf("%d%d",&n,&m);
		int x,y;
		bool f = true;
		if (n != m + 1) {
			f=false;
		}
		int cnt=0;
		for (int i = 0; i < m; i++) {
			scanf("%d%d", &x,&y);
			d[x]++, d[y]++;
			//add(x, y);
			//add(y, x);
		}
		if (!f) {
			cout << "No\n";
		}
		else {
			int md = 0;
			for (int i = 1; i <= n; i++) {
				md = max(md, d[i]);
			}
			if (md <= 2) {
				cout<<"Yes\n";
			}
			else {
				int cnt=0;
				for (int i = 1; i <= n; i++) {
					if (d[i] >= 3) {
						cnt++;
					}
				}
				if (cnt > 1) {
					cout<<"No\n";
				}
				else {
					cout<<"Yes\n";
				}
			}
		}

	}
}

10.笑声比赛

求最后一个xy前面的不同字符串数目,dp[i][j]表示前i个字符有长度为j的不同字符串数目
用last记录上一个arr[i]位置,如果last[arr[i]] = -1, 则dp[i][j] = dp[i-1][j] + dp[i-1][j-1],
否则dp[i][j] = dp[i-1][j] + dp[i-1][j-1]-dp[last[arr[i]] - 1][j-1]
在这里插入图片描述

#include<iostream>
#include<cstring>
#include<set>
#include<unordered_map>
#include<vector>
using namespace std;
const int N=1e3+5;
long long dp[N][N];
int mod=1e9+7;
int last[26];
char arr[N];
int main() {
	
	int t;
	scanf("%d",&t);
	while (t--) {
		memset(dp,0,sizeof dp);
		memset(last,-1,sizeof last);
		int n;
		scanf("%d",&n);
		/*string s;
		cin>>s;*/
		getchar();
		for (int i = 0; i < n; i++) {
			scanf("%c", &arr[i]);
		}
		dp[0][0] = 1;
		for (int i = 1; i <= n; i++) {
			dp[i][0] = 1;
			for (int j = 1; j <= i; j++) {
				//dp[i][j] = dp[i][j-1];
				if(last[arr[i-1] - 'a'] != -1)
					dp[i][j] = (dp[i - 1][j] + dp[i - 1][j - 1] - dp[last[arr[i - 1] - 'a'] - 1][j - 1] + mod) % mod;
				else {
					dp[i][j] = (dp[i - 1][j] + dp[i - 1][j - 1]) % mod;
				}
				
			}
			last[arr[i - 1] - 'a'] = i;
		}
		int q;
		scanf("%d",&q);
		unordered_map<char,int> mp;
		unordered_map<char, set<int>> ms;
		unordered_map<char, vector<int>> mv;
		for (int i = 1; i <= n; i++) {
			mp[arr[i-1]] = i;
			ms[arr[i-1]].insert(i);
			mv[arr[i-1]].push_back(i);
		}

		while (q--) {
			char x,y;
			int L;
			getchar();
			scanf("%c %c %d",&x,&y,&L);
			//cin>>x>>y>>L;
			if (!mp.count(x) || !mp.count(y)) {
				printf("0\n");
			}
			else {
				if (x == y) {
					if (mv[x].size() < 2) {
						printf("0\n");
					}
					else {
						printf("%d\n", dp[mv[x][mv[x].size() - 2] - 1][L - 2]);
					}
				}else{
					int y_id = mp[y];
					auto it = ms[x].upper_bound(y_id);
					if (it == ms[x].begin()) {
						printf("0\n");
					}
					else {
						it--;
						int x_id = *it;
						printf("%d\n", dp[x_id-1][L-2]);
					}
				}
			}
		}
	}
}

10.行走之谜
在这里插入图片描述

比赛都没看题,后面再补充吧

  • 7
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

qq_43983809

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值