Codeforces Round #740 Div. 2 解题报告

17 篇文章 0 订阅
9 篇文章 0 订阅

A. Simply Strange Sort

模拟
题意:
给你一个长度为 n n n ( n n n<=1000 且 n n n是奇数) 的排列;
有一种操作,定义第 i i i次操作:
如果 i i i是奇数,对所有奇数位置 p o s pos pos( p o s pos pos<n),如果 第 p o s pos pos位大于第 p o s pos pos+1位,那么交换。
如果 i i i是偶数,对所有偶数位置进行上面那个操作;
求在第几次操作之后,整个序列有序。
思路:
因为给的 n n n很小,对于一个数,他回到原本的位置,最多需要 n n n次操作,那么 n n n个数,最多 n ² n² n²次(虽然不可能会达到)。
复杂度:O( T * n * n );

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

int n,m;
int s[1001]; 
int main(){
	int t;
	scanf("%d",&t);
	while(t--){
		int ans=0;
		scanf("%d",&n);
		for(int i=1;i<=n;i++){
			scanf("%d",&s[i]);
		}
		while(1){
			int f=1; 
			for(int i=1;i<=n;i++)
				if(s[i]!=i)f=0;
			if(f)break;
			ans++;
			if(ans%2==1){
				for(int i=1;i<n;i+=2){
					if(s[i]>s[i+1])swap(s[i],s[i+1]);
				}
			}else for(int i=2;i<n;i+=2){
				if(s[i]>s[i+1])swap(s[i],s[i+1]);
			}
		} 
		printf("%d\n",ans);
	}
	return 0;
}

B. Charmed by the Game

题意:
一个游戏,两个人轮流先手,不告诉你谁在第一次游戏先手。如果玩家赢得了一局就会加一分。定义一个状态是,先手的玩家输掉了比赛。
告诉你最后两个玩家的得分 a a a b b b,求有多少个可能的上述状态存在,输出所有可能。
思路:
考虑枚举两个人第一局先手与否的不同情况,我们知道了谁在第一局先手,我们就知道了两个人先手的总局数,如果a先手但是得分比先手局数少,那么我们就直接知道了,a起码要在自己先手的情况下输几局。然后我们可以通过两个人在自己先手的局输,分别让对面赢,这样保证了在得分不变的情况下,对状态数贡献+2;
特殊情况:假赛的前提是,我让的分比他的得分少。
然后排序、去重、输出即可。

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

int n,m;
int s[1001]; 
vector<int>v;
int main(){
	int t;
	scanf("%d",&t);
	while(t--){
		int a,b;scanf("%d%d",&a,&b);
		n=a+b;
		v.clear();
		if(a==b){
			for(int i=0;i<=n;i+=2)
				v.push_back(i);
		}else{
			int p=n/2;
			int q=abs(p-a);
			int cnt=0;
			for(int i=q;i<=n;i+=2)
			{
				v.push_back(i);
				cnt++;
				if(cnt>min(a,b))break;
			}
			cnt=0;
			q=abs(p-b);
			for(int i=q;i<=n;i+=2)
			{
				v.push_back(i);
				cnt++;
				if(cnt>min(a,b))break;
			}	
		}
		sort(v.begin(),v.end());
		v.erase(unique(v.begin(),v.end()),v.end());
		printf("%d\n",v.size());
		for(int i=0;i<v.size();i++){
			printf("%d ",v[i]);
		}
		printf("\n");
	}
	return 0;
}

C. Deep Down Below

题意:
你是一名英雄,你有一个属性值:力量。怪物有一个属性值:护甲。
对于一个关卡,英雄有n个洞穴可以选择。要通过关卡,英雄必须以一定的顺序进入所有洞穴,每个洞穴只进入一次,并安全地离开每个洞穴。当英雄进入第一洞时,他会按顺序地和怪兽战斗:每个怪兽会有护甲值 a i ai ai;
当且仅当英雄的力量严格大于怪物的护甲时,英雄才能打败怪物。如果英雄不能打败他正在战斗的怪物,玩家输。
每次英雄击败怪物,英雄的力量增加1。
求最小的初始力量,使能够以某种顺序进入所有洞穴并击败所有怪物。
思路:
我们可以先预处理出所有洞穴能安全通过的最小力量。
然后排序并且合并。(如果两个洞穴的初始力量一样,那么能获得的力量增长是两个洞穴的怪物数之和)。
然后从对力量要求高的洞穴向要求低的洞穴覆盖即可。
最后答案对0取max。

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

int n,m;
int s[100040]; 
int k[100040];
struct node{
	int w,num; 
}p[100040];
struct no{
	int val,cnt;
}w[100040];
int cnt=0;
map<int,int>mp;
bool cmp(node a,node b){
	return a.w<b.w;
}
int main(){
	int t;
	scanf("%d",&t);
	while(t--){
		int ans=0;
		cnt=0;
		mp.clear();
		scanf("%d",&n);
		for(int i=1;i<=n;i++){
			scanf("%d",&k[i]);
			int res=0;
			for(int j=1;j<=k[i];j++){
				scanf("%d",&s[j]);
				res=max(res,s[j]-j+2);
			}
			p[i].w=res;p[i].num=k[i];
		}
		sort(p+1,p+1+n,cmp);
		for(int i=1;i<=n;i++){
			if(!mp[p[i].w])mp[p[i].w]=++cnt;
			w[mp[p[i].w]].cnt+=p[i].num;
			w[mp[p[i].w]].val=p[i].w;
		}
		for(int i=cnt;i>1;i--){
			w[i-1].val=max(w[i-1].val,w[i].val-w[i-1].cnt);
		}
		ans=max(ans,w[1].val);
		printf("%d\n",ans);
		for(int i=1;i<=cnt;i++)w[i].cnt=0;
	}
	return 0;
}

D1. Up the Strip (simplified version)

题意:
一个1*n 的网格,我们初始在n的位置,每一步有两个走法:
假设当前在x位置
1.向前走1 ~ x-1步;
2.选择一个z( 2 <= z <= x),走到 x / z (向下取整);
求走到1位置的方案数,对m取模;(对于第二个操作,选的数不同也认为是不同的操作);
思路:
很明显的 dp题。对于第一种操作,很明显,我们可以从所有 x + 1 x+1 x+1 ~ n n n转移过来,可以用 后缀和 优化 dp;
复杂的是第二个操作,暴力的话复杂度是 n 2 n^2 n2级别的,做不了。
但是对于 x / i x/i x/i 2 < = i < = x 2<=i<=x 2<=i<=x) 它具有很优美的性质,那就是 x / i x/i x/i向下取整在 1~ 根号x 是连续的,那么我们解不等式可以很简单的得到 取1 ~ 根号x 的范围;对于其他大于根号 x 的情况,我们再暴力的取2 ~ 根号 x 来转移即可。

ops;
害,很早就想到了这个做法,可惜中间有个变量没开 long long ,判断有没有大于 根号x 的时候炸了,气啊!好想上分。

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

ll n,m;
ll dp[200050];
ll sum=0;
int main(){
	scanf("%lld%lld",&n,&m);
	dp[n]=1;
	for(int i=n;i>=1;i--){
		dp[i]+=sum;
		dp[i]%=m;
		ll l=2,r=i;
		ll cnt=2;
		while(l<r){
			ll pos=i/cnt;
			while(pos>=r){
				cnt++;
				pos=i/cnt;
			}
			cnt++;
			pos++;
			while(pos<r&&((i/pos) != (i/r) ))
				pos++;
			dp[i/r]+=1LL*(r-pos+1)*dp[i];
			dp[i/r]%=m;
			r=pos-1;
			if(r*r<=i)break;
		}
		for(int j=2;j<=r;j++){
			dp[i/j]+=dp[i]; 
			dp[i/j]%=m;
		}
		sum+=dp[i];
		sum%=m;
	}
	printf("%lld\n",dp[1]);
	return 0;
}

D2. Up the Strip

题意:
这道题是上一道的加强版,
n < = 4 e 6 n<=4e6 n<=4e6
思路:
算了下复杂度,这道题对于第二种操作的转移是限制在 log 级的,现在还不是很有思路,明天补;

updated:
原来枚举一个数的倍数次,复杂度是 n l o g n nlogn nlogn的,赛时朝这个方向想了一下,但是不会算复杂度的我放弃了这个想法。
首先,D1我们的处理方式是枚举因子然后向下转移。
i < = x / j < i + 1 i<=x/j<i+1 i<=x/j<i+1;
我们从下向上的方式进行思考。
首先对于第一种操作,我们还是利用后缀和优化;

变换不等式,就变成了 i ∗ j < = x < i ∗ j + j i*j<=x<i*j+j ij<=x<ij+j;
对于第二种操作,我们发现对于一个 i i i我们可以枚举它的倍数 j j j,这样确定了 i i i j j j,我们就知道了他能转移过来的区间。因为他能从 i ∗ j 到 i*j到 ij i ∗ j + j i*j+j ij+j转移过来,我们用个后缀和数组就能实现这样的操作。
记得后端点和 n + 1 n+1 n+1取min;
欢乐ac。

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

ll n,m;
ll dp[4000050];
ll sum[4000050];
int main(){
	scanf("%lld%lld",&n,&m);
	dp[n]=1;
	sum[n+1]=0;
	for(ll i=n;i>=1;--i){
		dp[i]+=sum[i+1];
		dp[i]%=m;
		for(ll j=2;j*i<=n;++j){
			ll l=j*i;
			ll r=min(j*i+j,n+1);
			dp[i]+=(sum[l]-sum[r]);
			dp[i]%=m; 
		}
		sum[i]=sum[i+1]+dp[i];
		sum[i]%=m;
	}
	printf("%lld\n",dp[1]);
	return 0;
}

E. Bottom-Tier Reversals

题意:
给你一个排列,每次你可以选择一个奇数的结束位置 pos ,令 1 到 pos 位置翻转。问是否能在 5 ∗ n / 2 5*n/2 5n/2的次数内使数列有序。可以的话输出方案,不行的话输出 -1。
思路:
粗粗看了眼题目,感觉是要相邻奇数和偶数捆绑考虑的,因为我们翻转的右端点只能是奇数,那就意味着,一个偶数想去偶数位,那么就必须要保证他和他的下一位是贴贴的才行。明天补(有机会的话

ops:
第一眼看到还以为是平衡树裸题,可恶,区间翻转不是随便维护,可惜有限制。

评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值