AtCoder Beginner Contest 366 - VP记录

Preface

赛时基本都去做 E 去了,还好最后调出来了,不过还是太悬了些,最后 \(15\) 分钟才 A 掉。

我还是太菜了啊!绿题都有点吃力!

A - Election 2

高桥日常出镜,kkk 好好学学。

点击查看代码
#include<cstdio>
using namespace std;

int main()
{
	int n,t,a;
	scanf("%d%d%d",&n,&t,&a);
	if(t>n-t||a>n-a) printf("Yes\n");
	else printf("No\n");
	return 0;
}

B - Vertical Writing

某人被这道题卡了,默哀一秒钟。

内层循环要倒着遍历,小小地坑了我一下。

点击查看代码
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;

const int N=105;
int n;
char str[N][N];

int main()
{
	scanf("%d",&n);
	int h=0;
	for(int i=1;i<=n;i++)
	{
		scanf("%s",str[i]+1);
		h=max(h,(int)strlen(str[i]+1));
	}
	for(int i=1;i<=h;i++)
	{
		int cnt=0;
		for(int j=n;j>=1;j--)
		{
			if(str[j][i])
			{
				for(int k=1;k<=cnt;k++)
					putchar('*');
				cnt=0;
				putchar(str[j][i]);
			}
			else cnt++;
		}
		putchar('\n');
	}
	return 0;
}

C - Balls and Bag Query

原来 multisetsize() 返回的是元素个数(重复的算多个)。

模拟就好啦,加减的时候判断是不是少了或多了一种元素。

点击查看代码
#include<cstdio>
#include<set>
using namespace std;

const int X=1e6+5;
int q,cnt[X];

int main()
{
	scanf("%d",&q);
	int ans=0;
	for(int i=1;i<=q;i++)
	{
		int op; scanf("%d",&op);
		if(op==1)
		{
			int x; scanf("%d",&x);
			if(!(cnt[x]++)) ans++;
		}
		if(op==2)
		{
			int x; scanf("%d",&x);
			if(!(--cnt[x])) ans--;
		}
		if(op==3)
		{
			printf("%d\n",ans);
		}
	}
	return 0;
}

D - Cuboid Sum Query

好家伙,三维前缀和(题目名称说的很形象,就是长方体总和)。

没咋推,就跟着容斥原理的加减交替的形式,胡写一通就成了。

点击查看代码
#include<cstdio>
using namespace std;

const int N=105;
int n,a[N][N][N],sum[N][N][N],q;

int main()
{
	scanf("%d",&n);
	for(int i=1;i<=n;i++)
		for(int j=1;j<=n;j++)
			for(int k=1;k<=n;k++)
			{
				scanf("%d",&a[i][j][k]);
				sum[i][j][k] = a[i][j][k]
					+sum[i-1][j][k]+sum[i][j-1][k]+sum[i][j][k-1]
					-sum[i][j-1][k-1]-sum[i-1][j][k-1]-sum[i-1][j-1][k]
					+sum[i-1][j-1][k-1];
			}
	scanf("%d",&q);
	for(int i=1;i<=q;i++)
	{
		int x1,y1,z1,x2,y2,z2;
		scanf("%d%d%d%d%d%d",&x1,&x2,&y1,&y2,&z1,&z2);
		printf("%d\n",
			sum[x2][y2][z2]
			-sum[x1-1][y2][z2]-sum[x2][y1-1][z2]-sum[x2][y2][z1-1]
			+sum[x2][y1-1][z1-1]+sum[x1-1][y2][z1-1]+sum[x1-1][y1-1][z2]
			-sum[x1-1][y1-1][z1-1]
		);
	}
	return 0;
}

E - Manhattan Multifocal Ellipse

赛时基本都做这道题来了。

从左往右扫描所有点,每次扫描线向右移动一格,相当扫描线上每个点和左边所有点的距离都增加了 \(1\),和右边所有点的距离都缩短了 \(1\)(曼哈顿距离)。

然后我们只需要找到每次移动以后这根扫描线上总距离小于等于 \(d\) 的点就行了。

赛事乱画的草稿,可以辅助理解一下:

image

就是实现起来有点麻烦。

首先要把所有的坐标都转为正数,这个只需要全部加上一个常数就行了(我这里加的是 \(2\times10^6\),因为坐标系外也可能有合法的点)。

然后处理出对于某横坐标点数的前缀和,用于查找扫描线左右的点数。

接着我们需要知道对于初始位置(我这里是 \(0\))扫描线上每个位置距离所有点的曼哈顿距离和,这个有点小难度。

  • 初始位置扫描线上所有位置横坐标相等,所以直接枚举所有点,横坐标相加即可。

  • 纵坐标之差之和纵坐标有关,所以可以将所有点的纵坐标投影到初始位置扫描线上来。先找某位置与下面所有投影的距离和,具体来说:从下往上遍历所有位置,记录当前位置以下的投影数,在遍历的过程中每上移一格,总距离增加的就是这个点数。随后再找与上面所有投影的距离和,两者相加就可以得到纵坐标距离和。

  • 横坐标距离和加纵坐标距离和就是每个位置距离所有点的曼哈顿距离和。

最后,从左往右推扫描线。因为每次对线上距离的修改都是全局同加同减同查,所以只需要维护它变化的值。又因为全局加减不改变内部大小关系,所以可以事先排序。接着就可以二分查找扫描线上距离加变化之小于等于 \(d\) 的点的数量了,直接用 upper_bound 来查找也行。

#include<cstdio>
#include<algorithm>
using namespace std;

const int N=2e5+5,D=2e6;
#define D ((D<<1)+5)
int n,x[N],y[N];
int d,a[D];
int sumcnt[D];
long long ldis[D],rdis[D],dis[D];
int ycnt[D];
#undef D

inline int getsum(int l,int r)
{
	if(!l) return sumcnt[r];
	return sumcnt[r]-sumcnt[l-1];
}
int main()
{
	scanf("%d%d",&n,&d);
	for(int i=1;i<=n;i++)
	{
		scanf("%d%d",&x[i],&y[i]);
		x[i]+=D,y[i]+=D;
		a[x[i]]=y[i];
		sumcnt[x[i]]++;
	}
	for(int i=0;i<=D<<1;i++)
		sumcnt[i]+=sumcnt[i-1];
	
	long long xdis=0;
	for(int i=1;i<=n;i++)
	{
		xdis+=x[i];
		ycnt[y[i]]++;
	}
	long long lytot=0;
	for(int i=0;i<=D<<1;i++)
	{
		ldis[i]=(i?ldis[i-1]:0)+lytot;
		lytot+=ycnt[i];
	}
	long long rytot=0;
	for(int i=D<<1;i>=0;i--)
	{
		rdis[i]=rdis[i+1]+rytot;
		rytot+=ycnt[i];
	}
	for(int i=0;i<=D<<1;i++)
	{
		dis[i]=ldis[i]+rdis[i]+xdis;
	}
	sort(dis,dis+(D<<1)+1);
	
	long long ans=0,delta=0;
	for(int i=0;i<=D<<1;i++)
	{
		ans+=upper_bound(dis,dis+(D<<1)+1,d-delta)-dis;
		delta+=getsum(0,i);
		delta-=getsum(i+1,D<<1);
	}
	printf("%lld\n",ans);
	return 0;
}

F - Maximum Composition

赛后补的。

考虑两个线性函数 \(f_1(x) = a_1 x + b_1\)\(f_2(x) = a_2 x + b_2\),分别以 \(f_1,f_2\) 为内层合并到一起:

\[f_1(f_2(x)) = a_1 a_2 x + a_1 b_2 + b_1\\ f_2(f_1(x)) = a_1 a_2 x + a_2 b_1 + b_2 \]

可以发现两式仅有后面部分不同,且这部分与 \(x\) 的取值无关,所以我们就可以比较 \(a_1 b_2 + b_1\)\(a_2 b_1 + b_2\) 的大小来知道哪一个放在内层一定更优。通过这个排序后,就可以让保证选择序列时一定是按照顺序(正序或逆序,看上面的排序顺序)选择。

然后就是经典 DP 模型了,设 \(f_{i,j}\) 表示在前 \(i\) 个数中选 \(j\) 的最大答案,就有:

\[f_{i,j} = \max \left \{ \begin{aligned} &1\qquad\quad,j=0\\ &f_{i-1,j}\\ &a_i \times f_{i-1,j-1} + b_i \end{aligned} \right. \]

然后对所有的 \(f_{i,n}\) 取最大值就是答案了。

#include<cstdio>
#include<algorithm>
using namespace std;

const int N=2e5+5,K=15;
int n,k;
pair<int,int> p[N];
long long f[N][K];

bool cmp(pair<int,int> x,pair<int,int> y)
{
	return 1ll*x.first*y.second+x.second<1ll*y.first*x.second+y.second;
}
int main()
{
	scanf("%d%d",&n,&k);
	for(int i=1;i<=n;i++)
		scanf("%d%d",&p[i].first,&p[i].second);
	sort(p+1,p+n+1,cmp);
	long long ans=0;
	for(int i=1;i<=n;i++)
	{
		f[i-1][0]=1;
		for(int j=1;j<=k;j++)
			f[i][j]=max(f[i-1][j],f[i-1][j-1]*p[i].first+p[i].second);
		ans=max(ans,f[i][k]);
	}
	printf("%lld\n",ans);
	return 0;
}
posted @ 2024-10-29 17:25  Jerrycyx  阅读(8)  评论(0编辑  收藏  举报