ARC071简要题解

突然发觉我的flag里面好像还有“补完题写完简要题解”(悲
然后这都快半个月了居然只打了两场。怎么觉得墨水屏手机正在来的路上了(
来个ARC071的简要题解。

C题:
每个串开个桶,记录一下字母的出现次数,取个min,然后输出就好。

#include<bits/stdc++.h>
using namespace std;
char s[510];
int a[510][510];
int mi[510];
int n;
int main() { 
	memset(mi,0x3f,sizeof(mi));
	scanf("%d",&n);
	for(int i=1;i<=n;++i) { 
		scanf("%s",s+1);
		int m = strlen(s+1);
		for(int j=1;j<=m;++j) { 
			a[i][s[j]]++;
		} 
		for(int j='a';j<='z';++j) { 
			mi[j] = min(mi[j], a[i][j]);
		} 
	} 
	for(int i='a';i<='z';++i) { 
		for(int j=1;j<=mi[i];++j)
			printf("%c",i);
	} 
	return 0;
} 


D题:
考虑俩相邻直线间夹的那一个长条的贡献。
假设这俩相邻直线是平行于X,记录这条的宽度为len。
把上下边界卡死,问题就转化为了,给你数轴上的n个点,你可以任意从里头选线段,求sum_length。
枚举中点,左右乘法原理一下。
放开上下边界的那个限制,发现只要对x和y分别算一算,乘一块就好。

#include<bits/stdc++.h>
using namespace std;
const int mod = 1000000007;
int n,m;
long long x[100010], y[100010];
long long xxis[100010], yxis[100010];
int main() { 
	scanf("%d%d",&n,&m);
	for(int i=1;i<=n;++i) 
		scanf("%lld",&x[i]);
	for(int i=1;i<=m;++i) 
		scanf("%lld",&y[i]);
	for(int i=2;i<=n;++i) 
		x[i-1] = x[i] - x[i-1];
	for(int i=2;i<=m;++i)
		y[i-1] = y[i] - y[i-1];
	n--, m--;
	long long xsum = 0, ysum = 0;
	for(int i=1;i<=n;++i) { 
		xxis[i] = 1ll * i * (n-i+1) % mod;
		xsum = xsum + xxis[i] * x[i] % mod;
		xsum %= mod;
	} 
	for(int i=1;i<=m;++i) { 
		yxis[i] = 1ll * i * (m-i+1) % mod;
		ysum = ysum + yxis[i] * y[i] % mod;
		ysum %= mod;
	} 
	long long ans = xsum * ysum % mod;
	cout<<ans;
	return 0;
} 


E:
刚刚拿到手的时候愣了一下感觉没什么好办法。
手玩了一下,发觉可以通过如下路径交换相邻的俩AB:
A B − > B B B − > B B A A − > B A A A A − > B A AB -> BBB -> BBAA -> BAAAA -> BA AB>BBB>BBAA>BAAAA>BA
那么我们只要让这两个子串的AB数量对应相同就好
支持 A − > B B − > A A A A − > A A->BB->AAAA->A A>BB>AAAA>A这种线路,所以显然是对3取模。
发觉有三个等价类,类里头可以互相转换。
分别是:
(0,0)(1,1)(2,2)
(1,0)(0,2)(2,1)
(0,1)(2,0)(1,2)
然后对俩字母分别前缀和,每次询问卡出来,判一判两个是不是在同一个等价类就行。

#include<bits/stdc++.h>
using namespace std;
int n, m, T;
char s[100010], t[100010];
int sa[100010], sb[100010], ta[100010], tb[100010];
int l1,r1,l2,r2;
//swap good

int type(int a,int b) { 
	if(a==b) 
		return 1;
	if(a==1 && b==0)	
		return 2;
	if(a==0 && b==2) 
		return 2;
	if(a==2 && b==1) 
		return 2;
	return 3;
} 

int main() { 
	scanf("%s", s+1);
	scanf("%s", t+1);
	n = strlen(s+1), m = strlen(t+1);
	for(int i=1;i<=n;++i) { 
		sa[i] = sa[i-1], sb[i] = sb[i-1];
		if(s[i] == 'A')
			sa[i]++;
		else
			sb[i]++;
	} 
	for(int i=1;i<=m;++i) { 
		ta[i] = ta[i-1], tb[i] = tb[i-1];
		if(t[i] == 'A')
			ta[i]++;
		else
			tb[i]++;
	} 
	scanf("%d",&T);
	while(T--) { 
		scanf("%d%d%d%d",&l1,&r1,&l2,&r2);
		int a1 = sa[r1] - sa[l1-1];
		int b1 = sb[r1] - sb[l1-1];
		int a2 = ta[r2] - ta[l2-1];
		int b2 = tb[r2] - tb[l2-1];
		a1 %= 3, b1 %= 3, a2 %= 3, b2 %= 3;
		printf(type(a1,b1) == type(a2,b2) ? "YES\n" : "NO\n");
	} 
	return 0;
} 

F题:
妙妙的DP题。
首先发现一个事儿,如果你当前位置填了一个 i > 1 i>1 i>1,且下个位置还不填1,那么整个序列就固定了。这种情况可以直接被加进ans里去。
那么,可能产生其他状态的序列就肯定是长成这样的:
3 , 1 , 1 , 1 , 5 , 1....1 , 9 , 1 , 1.....1 , 2 , 1 , 1 , 17 , 1 , 1.....1 3,1,1,1,5,1....1,9,1,1.....1,2,1,1,17,1,1.....1 3,1,1,1,5,1....1,9,1,1.....1,2,1,1,17,1,1.....1
一个数字,一堆1,一个数字,一堆1这样。
定义一个dp状态,用dp[i]表示,我们填完了前i-1个位置,且可以在第i个位置任选一个数字填进去的方案数。
显然有一个n^2的dp方法,到了i,枚举i填个啥,然后对应转。
几种转移:
填1,在i+1处获得任选。
填k>1,在i+k+1处获得任选(假装他小等于n
填k>1,往后塞一堆1,并且这堆1覆盖了n,序列固定
填k>1,而后一位填写任意一个非1数,序列固定。
思考了一下发现,我们在dp[i]对之后的dp数组的贡献,一定是除了dp[i+2]之后的所有dp状态同时加上一个数字。
那么维护一个后缀plus,然后给dp[i+2]减去这东西就好。
然后就变O(n)的了。
代码里的注释是写题的时候写的,为了防止自己被绕晕hhh
注意 i = = n i==n i==n的时候,上面的 填非1再填非1 的转移是非法的,所以dp[n]要拿来特殊处理一下。

#include<bits/stdc++.h>
using namespace std;
int n, m, T;
const int mod = 1000000007;
long long dp[1000010];
long long ans = 0;
long long Splus = 0;
//当前做完i-1,且各自不同,第i个格子可以任选东西
//选1,在i+1处获得一个任选 1
//注意这里缺了一个dp[i+2]的任选位 1
//选k,而后在下一位填1,则在dp[i+k+1]处获得一个任选机会。 若i+k = n,则相当于钦定了一种ans 1
//选k,且k!=1,在一位填写任意一个非1数字,则固定了整个,直接加进ans。
//注意i = n-1的时候会发生变化。

int main() { 
	cin>>n;
	dp[1] = 1;
	for(int i=1;i<n;++i) { 
		dp[i] = dp[i] % mod + Splus;
		dp[i] %= mod, dp[i] += mod, dp[i] %= mod;
		long long pxis = i+1;
		if(i == n-1) pxis = i;
		ans += 1ll * pxis * dp[i] % mod;//当前位置填非1,然后填一堆1,然后1直接挤到n去了
		ans += 1ll * dp[i] * (n-1) % mod * (n-1) % mod;//当前位置填非1,下一个填非1,直接拉满
		ans %= mod;
		dp[i+2] -= dp[i], Splus += dp[i];//发放任选机会
		Splus %= mod;
	} 
	dp[n] = dp[n] + Splus; dp[n] %= mod;
	ans += dp[n] * n;
	ans %= mod, ans += mod, ans %= mod;
	cout<<ans;
} 


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
AtCoder Beginner Contest 134 是一场 AtCoder 的入门级比赛,以下是每道题的简要题解: A - Dodecagon 题目描述:已知一个正十二边形的边长,求它的面积。 解题思路:正十二边形的内角为 $150^\circ$,因此可以将正十二边形拆分为 12 个等腰三角形,通过三角形面积公式计算面积即可。 B - Golden Apple 题目描述:有 $N$ 个苹果和 $D$ 个盘子,每个盘子最多可以装下 $2D+1$ 个苹果,求最少需要多少个盘子才能装下所有的苹果。 解题思路:每个盘子最多可以装下 $2D+1$ 个苹果,因此可以将苹果平均分配到每个盘子中,可以得到最少需要 $\lceil \frac{N}{2D+1} \rceil$ 个盘子。 C - Exception Handling 题目描述:给定一个长度为 $N$ 的整数序列 $a$,求除了第 $i$ 个数以外的最大值。 解题思路:可以使用两个变量 $m_1$ 和 $m_2$ 分别记录最大值和次大值。遍历整个序列,当当前数不是第 $i$ 个数时,更新最大值和次大值。因此,最后的结果应该是 $m_1$ 或 $m_2$ 中较小的一个。 D - Preparing Boxes 题目描述:有 $N$ 个盒子和 $M$ 个物品,第 $i$ 个盒子可以放入 $a_i$ 个物品,每个物品只能放在一个盒子中。现在需要将所有的物品放入盒子中,每次操作可以将一个盒子内的物品全部取出并分配到其他盒子中,求最少需要多少次操作才能完成任务。 解题思路:首先可以计算出所有盒子中物品的总数 $S$,然后判断是否存在一个盒子的物品数量大于 $\lceil \frac{S}{2} \rceil$,如果存在,则无法完成任务。否则,可以用贪心的思想,每次从物品数量最多的盒子中取出一个物品,放入物品数量最少的盒子中。因为每次操作都会使得物品数量最多的盒子的物品数量减少,而物品数量最少的盒子的物品数量不变或增加,因此这种贪心策略可以保证最少需要的操作次数最小。

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值