ABC334题解(A~F)

前置

难度排序越来越阴间了。

[ABC334A] Christmas Present

题意简述

圣诞老人有一个 Bat 和一双 Glove,他会选一个价格高的送给你。给定两者的价格(保证不同),输出他会送你 Bat 还是 Glove

解题思路

直接输出即可。

代码示例

#include
using namespace std;
#define int long long
#define max(a,b) (a>b?a:b)
int a,b;
signed main(){
	cin>>a>>b;
	if(a>b) cout<<"Bat"<<endl;
	else cout<<"Glove"<<endl;
	return 0;
}

[ABC334B] Christmas Trees

题意简述

小F要摆放圣诞树。他先在 A A A 点摆一棵树。随后从 A A A 点开始,向左每隔 k k k 个单位长度摆一棵树,向右每隔 k k k 个单位长度摆一棵树。他想问你, l l l r r r 之间一共有几棵圣诞(包含 l , r l,r l,r)。

解题思路

A A A l , r l,r l,r 中间则查询 l , A l,A l,A 之间有多少棵,加上 A , r A,r A,r 之间有多少棵,再减一即可。

A A A l l l 左边则查询 A , r A,r A,r 之间有多少棵,减去 A , l − 1 A,l-1 A,l1 之间的棵数。

A A A r r r 右边则查询 l , A l,A l,A 之间有多少棵,减去 r + 1 , A r+1,A r+1,A 之间的棵树。

注意到我们这样定义的话,查询的一个端点一定是 A A A,那么就很好计算了。

代码示例

#include
using namespace std;
#define int long long
#define max(a,b) (a>b?a:b)
int A,k,l,r,ans=0;
int getans(int L,int to){
	return (to-L)/k+1;
}
signed main(){
	cin>>A>>k>>l>>r;
	if(l<=A&&A<=r) ans=getans(l,A)+getans(A,r)-1;
	else if(A<l) ans=getans(A,r)-getans(A,l-1);
	else ans=getans(l,A)-getans(r+1,A);
	cout<<ans<<endl;
	return 0;
}

[ABC334C] Socks 2

题意简述

n n n 双袜子,编号为 1 , 2 , ⋯   , n 1,2,\cdots,n 1,2,,n。现在丢失了 k k k 只袜子,分别为 a 1 , a 2 , ⋯   , a k a_1,a_2,\cdots,a_k a1,a2,,ak a a a 中的数两两不同。你要将剩余的袜子匹配成双(有可能最终余下一只),若将编号为 i , j i,j i,j 的袜子匹配会获得 max ⁡ ( i , j ) − min ⁡ ( i , j ) \max(i,j)-\min(i,j) max(i,j)min(i,j) 的不满意值。求不满意值最小为多少。

解题思路

不妨将袜子的编号抽象到数轴上,那么 i , j i,j i,j 袜子的不满意度就成了它们之间的距离。为什么要这么干呢?因为这样我们发现,对于 i , j i,j i,j 余下一只袜子, x x x 余下两只袜子时,若 i < x < j i < x < j i<x<j,那么无论怎么匹配不满意值都是一样的。

所以不妨将自身成双的袜子匹配掉,再匹配单只袜子,即匹配 a 1 , a 2 , ⋯   , a k a_1,a_2,\cdots,a_k a1,a2,,ak k k k 为偶数时显然相邻两个匹配最优。 k k k 为奇数时枚举扔掉哪个袜子,剩余的两两匹配。可以通过一个前缀和加后缀和维护扔掉袜子后匹配的不满意值(注意到给定的 a a a 数组是有序的)。

代码示例

#include
using namespace std;
#define int long long
#define max(a,b) (a>b?a:b)
int n,k,a[200010],num1[200010],num2[200010],ans=0;
signed main(){
	cin>>n>>k;
	for(int i=1;i<=k;i++) cin>>a[i];
	for(int i=2;i<=k;i+=2) num1[i]=num1[i-2]+a[i]-a[i-1];
	for(int i=k-1;i>=2;i-=2) num2[i]=num2[i+2]+a[i+1]-a[i];
	if(k%2==0) cout<<num1[k]<<endl;
	else{
		ans=0x3f3f3f3f3f3f3f3f;
		for(int i=1;i<=k;i++){
			if(i%2==1) ans=min(ans,num1[i-1]+num2[i+1]);
			else ans=min(ans,num1[i-2]+num2[i+2]+a[i+1]-a[i-1]);
		}
		cout<<ans<<endl;
	}
	return 0;
}

[ABC334D] Reindeer and Sleigh

题意简述

n n n 个雪橇,拉动第 i i i 个雪橇需要 a i a_i ai 头麋鹿。一头麋鹿只能拉一个雪橇。有 q q q 次询问,每次询问有 x x x 头麋鹿的情况下,最多能拉动多少雪橇。

解题思路

裸的贪心。显然我们想选择 a i a_i ai 小的雪橇先拉。所以将 a a a 先排个序,然后做一个前缀和,对于每次询问,二分查找最多能拉的雪橇数即可。

代码示例

#include
using namespace std;
#define int long long
#define max(a,b) (a>b?a:b)
int n,q,a[200010],num[200010];
signed main(){
	cin>>n>>q;
	for(int i=1;i<=n;i++) cin>>a[i];
	sort(a+1,a+n+1);
	for(int i=1;i<=n;i++) num[i]=num[i-1]+a[i];
	while(q--){
		int x;
		cin>>x;
		cout<<upper_bound(num+1,num+n+1,x)-num-1<<endl;
	}
	return 0;
}

[ABC334E] Christmas Color Grid 1

题意简述

给定一个 n n n m m m 列的矩阵,每个格子为 #.。定义这个矩阵的美丽值为 # 连通块的个数。# 连通块是指从这个 # 出发,往上下左右走能走到的最大的全部为 # 的区域。现在随机将一个 . 变成 #,求期望美丽值。

解题思路

考虑染色。对每个 # 连通块进行染色(打上序号),然后遍历所有的 .。如果这个 . 上下为 # 且序号不同,说明将这个 . 变成 # 后美丽值会少 1 1 1。左右、左上、左下、右上、右下情况类似,也就是说判断将其变成 # 后会把原来多少个不相连的 # 连通块相连。

代码示例

#include
using namespace std;
#define int long long
#define max(a,b) (a>b?a:b)
int n,m,vis[1010][1010],col[1010][1010],cnt=0,sum=0,ans=0,mod=998244353;
string s[1010];
int KSM(int a,int b){
	int ans=1;
	while(b){
		if(b&1) ans=ans*a%mod;
		a=a*a%mod;
		b>>=1;
	}
	return ans;
}
void dfs(int x,int y,int C){
//染色
	if(x<=0||y<=0||x>n||y>m) return;
	if(vis[x][y]||s[x][y]=='.') return;
	col[x][y]=C;
	vis[x][y]=1;
	dfs(x+1,y,C);
	dfs(x,y+1,C);
	dfs(x-1,y,C);
	dfs(x,y-1,C);
}
signed main(){
	cin>>n>>m;
	for(int i=1;i<=n;i++) cin>>s[i],s[i]=" "+s[i];
	for(int i=1;i<=n;i++){
		for(int j=1;j<=m;j++) if(!vis[i][j]&&s[i][j]=='#') dfs(i,j,++cnt);
	}
	for(int i=1;i<=n;i++){
		for(int j=1;j<=m;j++){
			if(s[i][j]=='.'){
				set<int> s;
				if(vis[i-1][j]) s.insert(col[i-1][j]);
				if(vis[i+1][j]) s.insert(col[i+1][j]);
				if(vis[i][j+1]) s.insert(col[i][j+1]);
				if(vis[i][j-1]) s.insert(col[i][j-1]);
                //查找能够把多少个原本不相连的连通块连起来
				sum=(sum+cnt+1-s.size())%mod;
				ans++;
			}
		}
	}
	cout<<(sum*KSM(ans,mod-2)%mod)<<endl;
	return 0;
}

[ABC334F] Christmas Present 2

题意简述

圣诞老人的家在 ( S x , S y ) (S_x,S_y) (Sx,Sy)。他要给 n n n 个编号为 1 , 2 , ⋯   , n 1,2,\cdots,n 1,2,,n 小朋友各送一个礼物,编号为 i i i 的小朋友在 ( x i , y i ) (x_i,y_i) (xi,yi)。圣诞老人的礼物袋最多只能装 k k k 个礼物,他可以选择在任何时刻回家将礼物袋中的礼物补满。他必须按小朋友的编号送礼物。求他给所有小朋友送完礼物并回家的路程最小值。

解题思路

考虑动态规划。 d p i , j dp_{i,j} dpi,j 表示当前到第 i i i 个小朋友,手中还剩 j j j 个礼物时的最短路程。显然有一个 n 2 n^2 n2 的做法,即 d p i , j = d p i − 1 , j + 1 + D i s ( i , i − 1 ) dp_{i,j}=dp_{i-1,j+1}+Dis(i,i-1) dpi,j=dpi1,j+1+Dis(i,i1)。其中 D i s ( i , j ) Dis(i,j) Dis(i,j) 表示从编号为 i i i 的小朋友处走到编号为 j j j 的小朋友处的距离。要注意 d p i , k dp_{i,k} dpi,k 的转移, d p i , k dp_{i,k} dpi,k 应该是从 d p i − 1 , j dp_{i-1,j} dpi1,j 中选一个最小的,加上 D i s ( i − 1 , 0 ) + D i s ( 0 , i ) Dis(i-1,0)+Dis(0,i) Dis(i1,0)+Dis(0,i)。假设圣诞老人的家编号为 0 0 0

考虑优化。发现有一堆东西是一样的,可以选择线段树优化。即,更新 d p i , j dp_{i,j} dpi,j 相当于区间加,更新 d p i , k dp_{i,k} dpi,k 相当于区间查询最小值。

代码示例

#include
using namespace std;
#define int long long
#define max(a,b) (a>b?a:b)
int n,k,x[200010],y[200010];
double Dis(int a,int b){
	return sqrt((x[a]-x[b])*(x[a]-x[b])+(y[a]-y[b])*(y[a]-y[b]));
}
struct node{
	double sum,lz;
	int l,r;
}t[400010<<2];
void Build(int rt,int l,int r){
	t[rt]={0,0,l,r};
	if(l==r) return;
	int mid=(l+r)>>1;
	Build(rt<<1,l,mid);
	Build(rt<<1|1,mid+1,r);
	t[rt].sum=min(t[rt<<1].sum,t[rt<<1|1].sum);
}
void Pushdown(int rt){
	int l=rt<<1,r=rt<<1|1;
	double ad=t[rt].lz;
	t[l].sum+=ad,t[l].lz+=ad;
	t[r].sum+=ad,t[r].lz+=ad;
	t[rt].lz=0;
}
void Update(int rt,int l,int r,double ad){
	if(t[rt].r<l||r<t[rt].l) return;
	if(l<=t[rt].l&&t[rt].r<=r){
		t[rt].lz+=ad;
		t[rt].sum+=ad;
		return;
	}
	Pushdown(rt);
	Update(rt<<1,l,r,ad);
	Update(rt<<1|1,l,r,ad);
	t[rt].sum=min(t[rt<<1].sum,t[rt<<1|1].sum);
}
double Query(int rt,int l,int r){
	if(t[rt].r<l||r<t[rt].l) return 1000000000000000000;
	if(l<=t[rt].l&&t[rt].r<=r) return t[rt].sum;
	Pushdown(rt);
	return min(Query(rt<<1,l,r),Query(rt<<1|1,l,r));
}
signed main(){
	cin>>n>>k;
	cin>>x[0]>>y[0];
    //圣诞老人的家
	for(int i=1;i<=n;i++) cin>>x[i]>>y[i];
	Build(1,1,400005);
	for(int i=1;i<=n;i++){
		double Min=Query(1,i,i+k-1),Add=0;
        //从前面找最小的
		if(i!=1) Add=Dis(0,i-1);
		Update(1,i+k,i+k,Min+Add+Dis(0,i));
        //更新 k
		Update(1,i+1,i+k-1,Dis(i-1,i));
        //更新 1~k-1
        //实际上整体完成了一次平移
	}
	printf("%.15lf\n",Query(1,n+1,n+k)+Dis(n,0));
	return 0;
}

你可能感兴趣的:(CF/ATC题解,算法,深度优先,图论)