Educational Codeforces Round 131 div.2 A-F题解

视频讲解:BV1bU4y1q7CB

A. Grass Field

题目大意

给定 2 × 2 2\times 2 2×2 的01矩阵,每次操作可以将一行一列的 1 1 1 变成 0 0 0 ,求最少需要几次操作,得到全零矩阵。

题解

简单模拟。
如果总和为 4 4 4 则需 2 2 2 次。
如果总和为 0 0 0 则需 0 0 0 次。
其余为 1 1 1 次。

参考代码

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;

int main()
{
	int T,a,b,c,d,sum;
	scanf("%d",&T);
	while(T--)
	{
		scanf("%d%d%d%d",&a,&b,&c,&d);
		sum=a+b+c+d;
		if(sum==4)
			puts("2");
		else if(sum==0)
			puts("0");
		else
			puts("1");
	}
}

B. Permutation

题目大意

对于排列 p p p 和给定正整数 d d d ,定义排列 p p p 的费用为满足 p i ⋅ d = p i + 1 p_i\cdot d=p_{i+1} pid=pi+1 i ( 1 ≤ i < n ) i(1 \leq i <n) i(1i<n) 的数量。

现在给定 n ( 2 ≤ n ≤ 2 ⋅ 1 0 5 ) n(2 \leq n \leq 2 \cdot 10^5) n(2n2105) ,求出一组费用最大的正整数 d d d 和排列 p p p

题解

首先可以想到,最优的 d d d 2 2 2
n = 6 n=6 n=6 时,一组最优解为

1 2 4 3 6 5

因此从小到大枚举,对于未出现过的数,逐个输出其 2 2 2 倍即可。

参考代码

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;

const int MAXN=200200;
int vis[MAXN];

int main()
{
	int T,n,i,j;
	scanf("%d",&T);
	while(T--)
	{
		scanf("%d",&n);
		memset(vis,0,sizeof(vis));
		puts("2");
		for(i=1;i<=n;i++)
		{
			for(j=i;j<=n&&!vis[j];j*=2)
			{
				printf("%d ",j);
				vis[j]=1;
			}
		}
		puts("");
	}
}

C. Schedule Management

题目大意

给定 n n n 个工人和 m m m 个任务。第 i i i 个任务如果由 a i a_i ai 号工人完成,消耗 1 1 1 小时吗,其他工人完成消耗 2 2 2 小时。

所有工人同时并行独立开始工作,每名工人同时只能完成一个工作。

求所有任务完成的最少消耗时间。

题解

直接二分答案。
注意开long long。

参考代码

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;

const int MAXN=200200;
int n,m;
int num[MAXN];

bool judge(int limit)
{
	ll undo=0;
	for(int i=1;i<=n;i++)
	{
		if(num[i]<=limit)
			undo-=(limit-num[i])/2;
		else
			undo+=num[i]-limit;
	}
	return undo<=0;
}

int main()
{
	int T,x,i,lef,rig,mid;
	scanf("%d",&T);
	while(T--)
	{
		scanf("%d%d",&n,&m);
		for(i=1;i<=n;i++)
			num[i]=0;
		for(i=1;i<=m;i++)
		{
			scanf("%d",&x);
			num[x]++;
		}
		lef=0;rig=2*m;
		while(lef<=rig)
		{
			mid=(lef+rig)/2;
			if(judge(mid))
				rig=mid-1;
			else
				lef=mid+1; 
		}
		printf("%d\n",lef);
	}
}

D. Permutation Restoration

题目大意

数组 b b b 由排列 a a a 通过 b i = ⌊ i a i ⌋ b_i=\lfloor \frac{i}{a_i} \rfloor bi=aii 得到。
给定长度为 n ( 1 ≤ n ≤ 5 ⋅ 1 0 5 ) n(1 \leq n \leq 5 \cdot 10^5) n(1n5105) b b b ,求出任意一个合法的 a a a 排列。

题解

b i = ⌊ i a i ⌋ b_i=\lfloor \frac{i}{a_i} \rfloor bi=aii 可得
a i b i ≤ i ≤ a i ( b i + 1 ) − 1 a_i b_i \leq i \leq a_i(b_i+1)-1 aibiiai(bi+1)1

b i = 0 b_i=0 bi=0
a i ≥ ⌈ i + 1 b i + 1 ⌉ = ⌊ i + b i + 1 b i + 1 ⌋ a_i\geq \lceil \frac{i+1}{b_i+1} \rceil=\lfloor \frac{i+b_i+1}{b_i+1} \rfloor aibi+1i+1=bi+1i+bi+1

b i ≠ 0 b_i \neq 0 bi=0
⌊ i + b i + 1 b i + 1 ⌋ ≤ a i ≤ ⌊ i b i ⌋ \lfloor \frac{i+b_i+1}{b_i+1} \rfloor \leq a_i \leq \lfloor \frac{i}{b_i} \rfloor bi+1i+bi+1aibii

由此,对于排列 a a a 上的每个位置,选择一个区间内的数填充。这是经典的贪心问题。
将所有区间按右端点排序,对于每个区间,选择区间内未被选中过的最小的数,填充到对应位置上。
选择区间内未被选中过的最小的数,可以采用set+lower_bound实现。

参考代码

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;

const int MAXN=500500;
int a[MAXN];
set<int> st;
set<int>::iterator it;

struct Node
{
	int l,r,id;
}seg[MAXN];

bool cmp(Node n1,Node n2)
{
	if(n1.r!=n2.r)
		return n1.r<n2.r;
	else
		return n1.l<n2.l;
}

int main()
{
	int T,n,i,x;
	scanf("%d",&T);
	while(T--)
	{
		scanf("%d",&n);
		st.clear();
		for(i=1;i<=n;i++)
		{
			scanf("%d",&x);
			seg[i].id=i;
			seg[i].l=(i+1+x)/(x+1);
			if(x)
				seg[i].r=i/x;
			else
				seg[i].r=n+1;
			st.insert(i);
		}
		sort(seg+1,seg+n+1,cmp);
		for(i=1;i<=n;i++)
		{
			it=st.lower_bound(seg[i].l);
			a[seg[i].id]=*it;
			st.erase(it);
		}
		for(i=1;i<=n;i++)
			printf("%d ",a[i]);
		puts("");
	}
}

E. Text Editor

题目大意

在文本编辑器上,通过若干次操作,将长度为 n n n 的字符串 s s s 删减为 长度为 m m m 的字符串 t t t
1 ≤ m ≤ n ≤ 5000 1 \leq m \leq n \leq 5000 1mn5000

初始光标在末尾,每次操作,可以选择以下动作之一:

  1. 将光标左移一位
  2. 将光标右移一位
  3. 将光标置于最左边
  4. 将光标置于最右边
  5. 删除光标左边的字符

求最少操作数。

题解

首先操作4将光标置于最右边,肯定是没用的。
关键考虑操作3将光标置于最左边。

设在 p o s pos pos 字符前,将光标置于最左边。此时 s [ p o s , n ] s[pos,n] s[pos,n] t t t 的某一后缀匹配,花费为 n − p o s + 1 n-pos+1 npos+1 ,最长匹配长度可以在 O ( N ) O(N) O(N) 复杂度内求出,设 s u f i suf_i sufi 表示 s [ i , n ] s[i,n] s[i,n] t t t 的后缀匹配的最大长度。

s u f 1 ≠ m suf_1\neq m suf1=m ,则无解输出 − 1 -1 1

然后进行操作3将光标置于最左边。

考虑求解 s [ 1 , i ] s[1,i] s[1,i] t [ 1 , j ] t[1,j] t[1,j] 匹配的最小代价,此时需满足后半部分也能对应匹配,即 s u f i + 1 + j > = m suf_{i+1}+j>=m sufi+1+j>=m
如果直接考虑删除,需要右移光标再删除,花费为 2 2 2
如果只是右移光标,花费为 1 1 1
如果 s [ 1 , i ] s[1,i] s[1,i] t [ 1 , j ] t[1,j] t[1,j] 的后缀匹配,则可以剩下最后一段的右移光标操作。
d p i , j dp_{i,j} dpi,j 表示 s [ 1 , i ] s[1,i] s[1,i] t [ 1 , j ] t[1,j] t[1,j] 的最长公共后缀长度,可以通过以下转移式或Z函数得到
d p i , j = { d p i − 1 , j − 1 + 1 s i = s j 0 s i ≠ s j dp_{i,j}=\begin{cases} dp_{i-1,j-1}+1 & s_i=s_j\\ 0 & s_i\neq s_j \end{cases} dpi,j={dpi1,j1+10si=sjsi=sj

如果有操作3将光标置于最左边,则花费为 1 + 2 i − j − d p i , j + n − i 1+2i-j-dp_{i,j}+n-i 1+2ijdpi,j+ni
如果没有,则花费为 n − i n-i ni

枚举所有 i , j i,j i,j ,求出最小花费即可。

参考代码

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;

const int MAXN=5050;
const int inf=1<<30;
char s[MAXN],t[MAXN];
int dp[MAXN][MAXN],suf[MAXN];

int main()
{
	int T,n,m,i,j,now,ans,com;
	scanf("%d",&T);
	while(T--)
	{
		scanf("%d%d",&n,&m);
		scanf("%s",s+1);
		scanf("%s",t+1);
		com=0;
		for(i=n;i>=1;i--)
		{
			if(com<m&&s[i]==t[m-com])
				com++;
			suf[i]=com;
		}
		if(suf[1]!=m)
		{
			puts("-1");
			continue;
		}
		for(i=1;i<=n;i++)
			for(j=1;j<=m;j++)
			{
				if(s[i]==t[j])
					dp[i][j]=dp[i-1][j-1]+1;
				else
					dp[i][j]=0;
			}
		ans=1e9;
		for(j=0;j<=m;j++)
		{
			com=0;
			for(i=0;i<=n;i++)
			{
				if(com<j&&s[i]==t[com+1])
					com++;
				if(com==j&&suf[i+1]+j>=m)
				{
					if(i==j)
						ans=min(ans,n-i);
					else
						ans=min(ans,1+2*i-j-dp[i][j]+n-i);
				}
			}
		}
		printf("%d\n",ans);			
	}
}

F. Points

题目大意

对于三元组 i , j , k i,j,k i,j,k ,若 i < j < k i<j<k i<j<k k − i ≤ d k-i\leq d kid ,则称其为漂亮的。
给定一个初始为空的集合,维护以下操作:

  • 输入一个 a i a_i ai 。若 a i a_i ai 在集合中,则删去。反之添加到集合。
  • 添加或删除后,输出总的漂亮三元组数。

题解

首先注意到,每个值必定是唯一的。

f i f_i fi 表示有多少 j j j 点在 [ i + 1 , i + d ] [i+1,i+d] [i+1,i+d] 范围内。
最终答案为 ∑ i f i 2 − f i 2 \sum_i \frac{f_i^2-f_i}{2} i2fi2fi

采用矩阵+线段树,维护 f i 2 f_i^2 fi2 f i f_i fi 即可。

参考代码引用ZongDeChuangSiYiGeBa 的AC代码

参考代码

#include<bits/stdc++.h>
#define For(i,x,y) for (int i=(x);i<=(y);i++)
#define FOR(i,x,y) for (int i=(x);i<(y);i++)
#define Dow(i,x,y) for (int i=(x);i>=(y);i--)
#define mp make_pair
#define fi first
#define se second
#define pb push_back
#define ep emplace_back
#define siz(x) ((int)(x).size())
#define all(x) (x).begin(),(x).end()
#define fil(a,b) memset((a),(b),sizeof(a))
using namespace std;
typedef long long ll;
typedef unsigned long long ull;
typedef pair<int,int> pa;
typedef pair<ll,ll> PA;
typedef vector<int> poly;
inline ll read(){
	ll x=0,f=1;char c=getchar();
	while ((c<'0'||c>'9')&&(c!='-')) c=getchar();
	if (c=='-') f=-1,c=getchar();
	while (c>='0'&&c<='9') x=x*10+c-'0',c=getchar();
	return x*f;
}

const int N = 2e5+10;
int q,n=2e5,d;
bool vis[N];

ll s0[N<<2],s1[N<<2],s2[N<<2],tag[N<<2];
inline void push_up(int u){
	s0[u]=s0[u<<1]+s0[u<<1^1];
	s1[u]=s1[u<<1]+s1[u<<1^1];
	s2[u]=s2[u<<1]+s2[u<<1^1];
}
inline void upd(int u,ll x){
	s2[u]+=2*x*s1[u]+x*x*s0[u];
	s1[u]+=x*s0[u];
	tag[u]+=x;
}
inline void push_down(int u){
	if (tag[u]){
		upd(u<<1,tag[u]);
		upd(u<<1^1,tag[u]);
		tag[u]=0;
	}
}
inline void Add(int u,int l,int r,int ql,int qr,ll x){
	if (l>=ql&&r<=qr) return upd(u,x),void(0);
	int mid=l+r>>1;push_down(u);
	if (ql<=mid) Add(u<<1,l,mid,ql,qr,x);
	if (qr>mid) Add(u<<1^1,mid+1,r,ql,qr,x);
	push_up(u);
}
inline void update(int u,int l,int r,int ql,ll x,ll y){
	if (l==r){
		s0[u]=x,s1[u]=x*y,s2[u]=x*y*y;
		return;
	}
	int mid=l+r>>1;push_down(u);
	if (ql<=mid) update(u<<1,l,mid,ql,x,y);
		else update(u<<1^1,mid+1,r,ql,x,y);
	push_up(u);
}
inline int Query(int u,int l,int r,int ql,int qr){
	if (l>=ql&&r<=qr) return s0[u];
	int mid=l+r>>1,ret=0;push_down(u);
	if (ql<=mid) ret+=Query(u<<1,l,mid,ql,qr);
	if (qr>mid) ret+=Query(u<<1^1,mid+1,r,ql,qr);
	return ret;
}

int main(){
	q=read(),d=read();
	For(i,1,q){
		int x=read();
		if (!vis[x]){
			ll tmp=Query(1,1,n,x,min(x+d,n));
			update(1,1,n,x,1,tmp);
			if (x>1) Add(1,1,n,max(1,x-d),x-1,1);
			vis[x]=1;
		} else {
			ll tmp=Query(1,1,n,x,min(x+d,n));
			update(1,1,n,x,0,0);
			if (x>1) Add(1,1,n,max(1,x-d),x-1,-1);
			vis[x]=0;
		}
		ll ans=(s2[1]-s1[1])/2;
		printf("%lld\n",ans);
	}
}
  • 2
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
"educational codeforces round 103 (rated for div. 2)"是一个Codeforces平台上的教育性比赛,专为2级选手设计评级。以下是有关该比赛的回答。 "educational codeforces round 103 (rated for div. 2)"是一场Codeforces平台上的教育性比赛。Codeforces是一个为程序员提供竞赛和评级的在线平台。这场比赛是专为2级选手设计的,这意味着它适合那些在算法和数据结构方面已经积累了一定经验的选手参与。 与其他Codeforces比赛一样,这场比赛将由多个问题组成,选手需要根据给定的问题描述和测试用例,编写程序来解决这些问题。比赛的时限通常有两到三个小时,选手需要在规定的时间内提交他们的解答。他们的程序将在Codeforces的在线评测系统上运行,并根据程序的正确性和效率进行评分。 该比赛被称为"educational",意味着比赛的目的是教育性的,而不是针对专业的竞争性。这种教育性比赛为选手提供了一个学习和提高他们编程技能的机会。即使选手没有在比赛中获得很高的排名,他们也可以从其他选手的解决方案中学习,并通过参与讨论获得更多的知识。 参加"educational codeforces round 103 (rated for div. 2)"对于2级选手来说是很有意义的。他们可以通过解决难度适中的问题来测试和巩固他们的算法和编程技巧。另外,这种比赛对于提高解决问题能力,锻炼思维和提高团队合作能力也是非常有帮助的。 总的来说,"educational codeforces round 103 (rated for div. 2)"是一场为2级选手设计的教育性比赛,旨在提高他们的编程技能和算法能力。参与这样的比赛可以为选手提供学习和进步的机会,同时也促进了编程社区的交流与合作。

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值