ARC081简要题解

昨天因为沈阳场真的去不成了,心态有点爆炸,就鸽了一天。而当鸽子是有代价的,所以今晚搞不好还有一场。
打的并不是很开心,T3的字典序想了好久也没弄出来,T4的悬线法太久没写,脑补的时候差点给我人送走。
赛中三题然后跑路了。
在这里插入图片描述
C:
开个map记录一下出现至少2、4次的最大次大值,然后判一判就完了。

#include<bits/stdc++.h>
using namespace std;
int num[200010];
int tnum[200010], fnum[200010];
int n, tcnt, fcnt;
int tma, tmm, foma;
map<int, int>sth;
int main() { 
	scanf("%d", &n);
	for(int i=1;i<=n;++i) 
		scanf("%d", &num[i]), sth[num[i]]++;
	for(int i=1;i<=n;++i) { 
		int nw = num[i];
		if(sth[nw] >= 4) {
			foma = max(foma, nw);
		} 
		if(sth[nw] >= 2) { 
			tma = max(tma, nw);
		} 
	} 
	for(int i=1;i<=n;++i) { 
		int nw = num[i];
		if(sth[nw] >= 2 && nw != tma) { 
			tmm = max(tmm, nw);
		} 
	}
	long long ans = max(1ll * foma * foma, 1ll * tmm * tma);
	cout<<ans;
	return 0;
} 

D:
发觉他并不会有图中左边这种情况,那么单独考虑一列,要么是一块,要么是两块。
在这里插入图片描述

记录一下每个地方到底是一块竖着的还是两块拼起来,判一判乘一乘就完事了。

#include<bits/stdc++.h>
using namespace std;
char s[3][100];
int n;
const int mod = 1000000007;
int trans[310], loc;
int main() { 
	scanf("%d", &n);
	scanf("%s", s[1] + 1);
	scanf("%s", s[2] + 1);
	for(int i=1;i<=n;) { 
		if(s[1][i] == s[2][i]) {
			trans[++loc] = 1;
			++i;
			continue;
		} 
		if(s[1][i] != s[2][i]) { 
			trans[++loc] = 2;
			i+=2;
			continue;
		} 
	} 
	long long ans = 1;
	if(trans[1] == 1) 
		ans = 3;
	else 
		ans = 6;
	for(int i=2;i<=n;++i) { 
		int pre = trans[i-1], nw = trans[i];
		if(pre == 1 && nw == 1) { 
			ans = ans * 2;
			ans %= mod;
		} 
		else if(pre == 2 && nw == 1) { 
			ans = ans * 1;
			ans %= mod;
		} 
		else if(pre == 1 && nw == 2) { 
			ans = ans * 2;
			ans %= mod;
		} 
		else if(pre == 2 && nw == 2) { 
			ans = ans * 3;
			ans %= mod;
		} 
	} 
	cout<<ans;
	return 0;
} 

E题:
判长度很好办,直接开个桶for过去,一轮一轮取就好。
但是字典序最小emmm,想了很久都没救,就弃疗去看F了。
题解爸爸表示,知道了长度并没有什么卵用,你需要观察出这玩意满足最优子结构。
p o s [ i ] [ j ] pos[i][j] pos[i][j]为,从 i i i位置开始往右看,字母 j j j第一次出现的位置下标。
状态为 d p [ i ] dp[i] dp[i]表示,在长度为 [ i , n ] [i, n] [i,n]这个后缀里面,不在里面的子序列的长度。
转移: d p [ i ] = m i n ( d p [ p o s [ i ] [ j ] + 1 ] + 1 ) dp[i] = min(dp[pos[i][j]+1]+1) dp[i]=min(dp[pos[i][j]+1]+1)
字典序:从 d p [ 1 ] dp[1] dp[1]for下去,每次找到最靠前,值对得上的转移走过去,输出就完事。

#include<bits/stdc++.h>
using namespace std;
char s[200010];
int pos[200010][140];
int dp[200010];
int n;
int main() { 
	scanf("%s", s+1);
	n = strlen(s+1);
	memset(pos,0x3f,sizeof(pos));
	for(int i=n;i>=1;--i) { 
		for(int j='a';j<='z';++j) 
			pos[i][j] = pos[i+1][j];
		pos[i][s[i]] = i;
	} 
	for(int i=n+1;i<=n+5;++i) 
		dp[i] = 1;
	for(int i=n;i>=1;--i) { 
		dp[i] = 0x3f3f3f3f;
		for(int j='a';j<='z';++j) { 
			int tar = pos[i][j];
			if(tar > n) { 
				dp[i] = 1;
				break;
			} 
			else
				dp[i] = min(dp[i], dp[tar+1] + 1);
		} 
	} 
	int nw = 1;
	while(nw <= n) { 
		for(int i='a';i<='z';++i) { 
			int tar = pos[nw][i];
			if(tar > n || dp[tar + 1] == dp[nw] - 1) { 
				printf("%c", i);
				nw = tar + 1;
				break;
			} 
		} 
	} 
	return 0;
} 

F题:
拿到之后感觉完全没想法,冷静了一下然后开始搞性质。
首先显然翻转的先后顺序没啥关系,一个格子只由他对应的行和列决定状态。
其次,容易发现,如果一个 2 × 2 2\times2 2×2的矩形里头有奇数个某种颜色,那么他铁定不可能全黑(奇偶性),称之为非法。
再其次,如果对于某一坨块儿矩形,它内部的每个 2 × 2 2\times2 2×2都不非法,那么肯定有办法让整个矩形全黑,这里考虑一个异或方程组的解就好,简单手玩了几组,也都有这个结论。
然后把每个 2 × 2 2\times2 2×2的左上角作为代表元,跑最大全 1 1 1矩形。
就是这悬线法,直接写没了我半条命。貌似上次写这玩意还是高二?
凭借模糊的远古记忆现场脑补算法.jpg

#include<bits/stdc++.h>
using namespace std;
int mat[2010][2010];
bool vis[2010][2010];
int lef[2010][2010], rig[2010][2010], upto[2010][2010];
char s[12313];
int n, m;
int main() { 
	scanf("%d%d", &n, &m);
	for(int i=1;i<=n;++i) { 
		scanf("%s", s+1);
		for(int j=1;j<=m;++j) { 
			mat[i][j] = s[j] == '#';
		} 
	} 
	
	for(int i=1; i < n; ++i) 
		for(int j=1; j < m; ++j) 
			vis[i][j] = mat[i][j] ^ mat[i+1][j] ^ mat[i][j+1] ^ mat[i+1][j+1];
			
	for(int i = 1; i < n; ++i) { 
		for(int j = 1; j < m; ++j) 
			lef[i][j] = vis[i][j] == 0 ? lef[i][j-1] + 1 : 0;
		for(int j = m - 1; j >= 1; --j) 
			rig[i][j] = vis[i][j] == 0 ? rig[i][j+1] + 1 : 0;
	} 
	
	int ans = max(n, m);
	for(int j=0;j<=m;++j)
		lef[0][j] = rig[0][j] = 123123;
	
//	for(int i=1;i<n;++i) { 
//		for(int j=1;j<m;++j) { 
//			printf("%3d ", lef[i][j]);
//		} 
//		printf("\n");
//	}
//	printf("\n");
//	for(int i=1;i<n;++i) {
//		for(int j=1;j<m;++j) {
//			printf("%3d ", rig[i][j]);
//		}
//		printf("\n");
//	}
//	printf("\n");

	for(int i = 1; i < n; ++i) { 
		for(int j = 1; j < m; ++j) { 
			if(vis[i][j]) { 
				upto[i][j] = 0;
				lef[i][j] = rig[i][j] = 1231231;
			} 
			else { 
				upto[i][j] = upto[i-1][j] + 1;
				lef[i][j] = min(lef[i][j], lef[i-1][j]);
				rig[i][j] = min(rig[i][j], rig[i-1][j]);
				ans = max(ans, (upto[i][j] + 1) * (lef[i][j] + rig[i][j]));
			} 
		} 
	} 
	cout<<ans;
	return 0;
} 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值