AtCoderABC312 C~E 题题解

[ABC312C]Invisible Hand 题解

感觉在所有 C 题中算难的。

首先我们发现由于题中的 x x x 是用来比较的,在此条件下,我们只关心 x x x 的排名。

进一步地,我们发现只要选用 a i a_i ai a i + 1 a_i+1 ai+1 可以表示出所有的排名( b i b_i bi 也是一个道理),而且这样是使 x x x 最小的,因为你大了排名没变,不值。

所以我们只需要尝试 a i a_i ai a i + 1 a_i+1 ai+1 b i b_i bi b i + 1 b_i+1 bi+1 找出答案即可。

怎么判断正确性呢?由于题目相当于是在找排序后的一段前缀或者一段后缀,所以我的思路是排序之后使用 STL 的二分查找函数解决。

#include<bits/stdc++.h>
#define LL long long
using namespace std;
const LL N=3e5+5;
 
LL n,m,a[N],b[N],ans=1e18;
bool pd(LL x)
{
	LL cnt1=upper_bound(a+1,a+n+1,x)-a-1;
	LL cnt2=m-(lower_bound(b+1,b+m+1,x)-b-1);
	return cnt1>=cnt2;
}
int main()
{
	scanf("%lld%lld",&n,&m);
	for(int i=1;i<=n;i++)
	{
		scanf("%lld",&a[i]);
	}
	for(int i=1;i<=m;i++)
	{
		scanf("%lld",&b[i]);
	}
	sort(a+1,a+n+1);
	sort(b+1,b+m+1);
	for(int i=1;i<=n;i++)
	{
		if(pd(a[i]))ans=min(ans,a[i]);
		if(pd(a[i]+1))ans=min(ans,a[i]+1);
	}
	for(int i=1;i<=m;i++)
	{
		if(pd(b[i]))ans=min(ans,b[i]);
		if(pd(b[i]+1))ans=min(ans,b[i]+1);
	}
	printf("%lld",ans);
}

[ABC312D]Count Bracket Sequences 题解

括号序列的 Trick 我比赛前一天刚好比赛碰到了,而且是三道啊三道,所以这题秒了。

我们可以考虑进行一个转化,把左括号看成 1 1 1,右括号看成 − 1 -1 1,这样合法括号序列可以看成任意前缀和非负,区间和为 0 0 0 的序列。

对于这道题,不难想到设 f i , j f_{i,j} fi,j 表示前 i i i 项前缀和为 j j j 的方案数。

保证前缀和非负很简单,我们所有负的前缀和看作不合法状态即可,区间和为 0 0 0 也很简单,这说明了我们最终需要的答案状态为 f n , 0 f_{n,0} fn,0

考虑转移,转移非常简单,对于确定项,直接按照其值找到前面的状态转移到我们当前状态即可。

对于未知项,可以枚举其代表的值,将两种情况累加即可。

注意判一手前面状态的合法性。

时间复杂度为 O ( n 2 ) \mathcal O(n^2) O(n2)

#include<bits/stdc++.h>
#define LL long long
using namespace std;
const LL N=3e3+5;
const LL mod=998244353;
LL n,f[N][N];
char c[N];
int main()
{
	cin>>(c+1);
	n=strlen(c+1);
	f[0][0]=1;
	for(int i=1;i<=n;i++)
	{
		for(int j=0;j<=n;j++)
		{
			if(c[i]=='?')
			{
				if(j-1>=0)f[i][j]+=f[i-1][j-1];
				f[i][j]+=f[i-1][j+1];
			}
			else if(c[i]=='(')
			{
				if(j-1>=0)f[i][j]+=f[i-1][j-1];
			}
			else
			{
				f[i][j]+=f[i-1][j+1];
			}
			f[i][j]%=mod;
		}
	}
	cout<<f[n][0]<<endl;
}

[ABC312E]Tangency of Cuboids 题解

鸡汤

懂得很多 Trick,但还是过不好这道题。

——DengDuck

技巧都想到了,但是方向错了,寄。

题解

首先我们分析这道题的性质:

  • 立方体交的体积为 0 0 0
  • 立方体数量多,值域小。
  • 立方体的坐标都是整点。

我们考虑切割立方体,分成 1 × 1 × 1 1\times 1\times 1 1×1×1 的正方体,我们用三个量编号, ( x , y , z ) (x,y,z) (x,y,z) 表示 ( x , y , z ) (x,y,z) (x,y,z) ( x + 1 , y + 1 , z + 1 ) (x+1,y+1,z+1) (x+1,y+1,z+1) 的正方体由于立方体交的体积为 0 0 0,所以对于每个正方体都最多拥有一个唯一的主人,我们把主人的标记打上去。

由于这个值域很小,所以枚举小正方体完全可行的。

我们思考,题目让我们求对于每个立方体面上交面积不为 0 0 0 的立方体数量,那么,这个立方体显然再小正方形上也是有交的,而对于小正方体,有交的立方体就是相邻的小正方体。

也就是说,对于我们某一个立方体所有的小正方体,我们都可以通过找其相邻的小正方体来找与其面积有交的立方体,但是对于每个立方体很可能被统计多次,所以我们可以用 set 来维护统计到的项。

最后每个答案就是对应的 set 的集合大小。

代码

#include<bits/stdc++.h>
#define LL long long
using namespace std;
const LL N=3e5+5;
LL n,x[N][2],y[N][2],z[N][2],a[205][205][205];
set<LL>ans[N];
void work(LL i)
{
	for(LL xx=x[i][0];xx<x[i][1];xx++)
	{
		for(LL yy=y[i][0];yy<y[i][1];yy++)
		{
			for(LL zz=z[i][0];zz<z[i][1];zz++)
			{
				a[xx][yy][zz]=i;
			}				
		}		
	}
}
void add(LL i,LL j)
{
	ans[i].insert(j);
	ans[j].insert(i);
}
int main()
{
	scanf("%lld",&n);
	for(int i=1;i<=n;i++)
	{
		scanf("%lld%lld%lld",&x[i][0],&y[i][0],&z[i][0]);
		scanf("%lld%lld%lld",&x[i][1],&y[i][1],&z[i][1]);
		work(i);
	}
	for(int i=0;i<100;i++)
	{
		for(int j=0;j<100;j++)
		{
			for(int k=0;k<100;k++)
			{
				if(!a[i][j][k])continue;
				if(a[i+1][j][k]&&a[i][j][k]!=a[i+1][j][k])add(a[i][j][k],a[i+1][j][k]);
				if(a[i][j+1][k]&&a[i][j][k]!=a[i][j+1][k])add(a[i][j][k],a[i][j+1][k]);
				if(a[i][j][k+1]&&a[i][j][k]!=a[i][j][k+1])add(a[i][j][k],a[i][j][k+1]);
			}
		}
	}
	for(int i=1;i<=n;i++)
	{
		printf("%d\n",ans[i].size());
	}
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值