Codeforces Round #821 (Div. 2)(A~E)

A. Consecutive Sum

最多可以进行k次操作,每次操作可以将两个对k取模相等的下标对应的元素交换位置,问做完这些操作之后每连续的k个数中,最大的和是多少。

思路:数据范围很小,直接对于所有对k取模相等的数进行比较,取最大的即可。

AC Code:

#include <bits/stdc++.h>

typedef long long ll;
const int N=1e5+5;
int t,n,k;
ll a[N];

int main(){
	std::ios::sync_with_stdio(false);
	std::cin.tie(0);
	std::cout.tie(0);
	std::cin>>t;
	while(t--){
		std::cin>>n>>k;
		for(int i=1;i<=n;i++){
			std::cin>>a[i];
		}
		ll ans=0;
		for(int i=1;i<=k;i++){
			ll max=-1;
			for(int j=0;i+j*k<=n;j++){
				max=std::max(max,a[i+j*k]);
			}
			ans+=max;
		}
		std::cout<<ans<<'\n';
	}
	return 0;
}

 B. Rule of League

一列人,从头开始,两两比赛,胜出的人留下,每个人获胜的数量只能是x或y,若存在满足条件的胜负状态则输出任意一种,否则输出-1。

思路:首先如果一共n个人的获胜次数凑不出来n-1,则输出-1;若可以凑出来,则从头开始赋值即可。

AC Code:

#include <bits/stdc++.h>

typedef long long ll;
const int N=1e5+5;
int t;
ll n,x,y;

int main(){
	std::ios::sync_with_stdio(false);
	std::cin.tie(0);
	std::cout.tie(0);
	std::cin>>t;
	while(t--){
		std::cin>>n>>x>>y;
		bool flag=false;
		int cntx,cnty;
		for(int i=0;i<n;i++){
			if(x*i+(n-i)*y==n-1){
				flag=true;
				cntx=i,cnty=n-i;
				break;
			}
		}
		if(!flag){
			std::cout<<-1<<'\n';
			continue;
		}
		std::vector<int>ans;
		int pos=1,cnt=0;
		for(int i=1;i<=cntx;i++){
			for(int j=1;j<=x;j++){
				ans.push_back(pos);
				cnt++;
			}
			pos=cnt+2;
		}
		for(int i=1;i<=cnty;i++){
			for(int j=1;j<=y;j++){
				ans.push_back(pos);
				cnt++;
			}
			pos=cnt+2;
		}
		int len=ans.size();
		for(int i=0;i<len;i++){
			std::cout<<ans[i]<<" \n"[i==len-1];
		}
	}
	return 0;
}

C. Parity Shuffle Sorting

在一个数组中,可以使用最多n次操作,选择两个数字,若两数加起来为奇数,则将位置靠后的数字改为另一个数;若为偶数,则将位置靠前的数改为前一个数,怎样操作使得在最多n次操作之后使数列变为非递减排序的。

思路:一般这种题还有什么交互题,都应该去考虑比较极端的情况。这个题可以这样考虑:将第一个数和最后一个数经过一次操作变成一样的,中间的数,如果加起来是奇数的话选第一个数,如果加起来是偶数的话选最后一个数,这样在n-1次操作后将整个序列变成一样的数字,满足条件。(注意1特判,也可能不用?)

AC Code:

#include <bits/stdc++.h>

typedef long long ll;
typedef std::pair<int,int>PII;
const int N=1e5+5;
int t,n;
ll a[N];

int main(){
	std::ios::sync_with_stdio(false);
	std::cin.tie(0);
	std::cout.tie(0);
	std::cin>>t;
	while(t--){
		std::cin>>n;
		for(int i=1;i<=n;i++){
			std::cin>>a[i];
		}
		if(n==1){
			std::cout<<0<<'\n';
			continue;
		}
		std::vector<PII>ans;
		ans.push_back({1,n});
		int pos;
		if(a[1]+a[n]&1) pos=a[1];
		else pos=a[n];
		for(int i=2;i<n;i++){
			if(a[i]+pos&1) ans.push_back({1,i});
			else ans.push_back({i,n});
		}
		int len=ans.size();
		std::cout<<len<<'\n';
		for(int i=0;i<len;i++){
			std::cout<<ans[i].first<<' '<<ans[i].second<<'\n';
		}
	}
	return 0;
}

 D1. Zero-One (Easy Version)

给出a和b两个字符串,现对a进行操作,选择两个位置,翻转这两位的数字, 若两位置相邻,则花费为x,否则花费为y,现在求令两个字符串变为相同时的最少花费,若无法变为相同,则输出-1。

思路:首先当不同的字符是奇数个时,是无法变成相同的字符串,输出-1;因为D1保证了x>=y,所以尽可能不同时修改相邻的位置。当两个相邻且仅有这两个,且x<2*y时,我们必须选择修改相邻的位置;否则,首先修改不相邻的。可以证明除了这种情况外其他的情况都可以使用y解决。

AC Code:

#include <bits/stdc++.h>

#define int long long
int t,n,x,y;
std::string a,b;

signed main(){
    std::ios::sync_with_stdio(false);
    std::cin.tie(0);
    std::cout.tie(0);
    std::cin>>t;
    while(t--){
        std::cin>>n>>x>>y;
        std::cin>>a>>b;
        int cnt=0;
        for(int i=0;i<n;i++){
            if(a[i]!=b[i]) cnt++;
        }
        if(cnt&1){
            std::cout<<-1<<'\n';
            continue;
        }
        bool flag=false;
        for(int i=0;i<n-1;i++){
            if(a[i]!=b[i]&&a[i+1]!=b[i+1]){
                if(cnt==2){
                    flag=true;
                    break;
                }
            }
        }
        if(flag) std::cout<<std::min(x,2*y)<<'\n';
        else std::cout<<cnt/2*y<<'\n';
    }
    return 0;
}

os:注意开ll

D2. Zero-One (Hard Version)

与D1题意类似,只是不再保证x>=y,且数据范围变成5e3。

思路:特判的情况与D1差不多,主要就是x<y,且有多个不同位置时的讨论。

思路是知乎严格鸽的,orzorzCodeforces Round #821 (Div. 2) D(dp) E(思维) - 知乎 (zhihu.com)

AC Code:

#include <bits/stdc++.h>

#define int long long
const int N=5e3+5;
int t,n,x,y;
int f[N][N];
std::string a,b;
std::vector<int>vec;

int getans(int l,int r){
    if(l+1==r) return std::min(2*y,x);
    else return std::min(y,x*(r-l));
}

int DFS(int l,int r){
    if(l>r) return 0;
    if(f[l][r]!=-1) return f[l][r];
    int res=1e18;
    res=std::min(res,DFS(l+1,r-1)+getans(vec[l],vec[r]));
    res=std::min(res,DFS(l,r-2)+getans(vec[r-1],vec[r]));
    res=std::min(res,DFS(l+2,r)+getans(vec[l],vec[l+1]));
    return f[l][r]=res;    
}

signed main(){
    std::ios::sync_with_stdio(false);
    std::cin.tie(0);
    std::cout.tie(0);
    std::cin>>t;
    while(t--){
        std::cin>>n>>x>>y;
        std::cin>>a>>b;
        a=' '+a,b=' '+b;
        vec.clear();
        for(int i=0;i<=n;i++){
            for(int j=0;j<=n;j++)
                f[i][j]=-1;
        }
        for(int i=1;i<=n;i++){
            if(a[i]!=b[i])
                vec.push_back(i);
        }
        if(vec.size()%2==1){
            std::cout<<-1<<'\n';
            continue;
        }
        if(vec.size()==0){
            std::cout<<0<<'\n';
            continue;
        }
        if(vec.size()==2){
            if(vec[0]+1==vec[1]) std::cout<<std::min(2*y,x)<<'\n';
            else std::cout<<std::min(y,x*(vec[1]-vec[0]))<<'\n';
            continue;
        }
        if(y<=x) std::cout<<vec.size()/2*y<<'\n';
        else std::cout<<DFS(0,vec.size()-1)<<'\n';
    }
    return 0;
}

E. Conveyor

给出一个无限大的网格,初始时每个网格的方向都是向右的,每一秒都会在(0,0)处放一个箱子,这个箱子会向现在所处网格的方向移动一格,然后网格方向变为向下,即向右和向下交替,q组询问,每次给出一个格子和时间t,判断在时间t时该格子是否有箱子。

思路:直接计算ts时每一个位置的格子不容易,但是可以计算ts时某一个格子途径了几个箱子,用两个时间的变化量也可以表示。我们定义a[i][j]在ts时,经过了多少箱子。可以知道,a[0][0]=t+1,又因为初始方向为向右,所以该点向右传送了\left \lceil \frac{a[i][j]}{2} \right \rceil向下传送了\left \lfloor \frac{a[i][j]}{2} \right \rfloor,这样计算在两个时间之间的差值,注意,该位置的点在到达该位置之前也至少有i+j的时间。

AC Code:

#include <bits/stdc++.h>

#define int long long
typedef long long ll;
const int N=150;
int f[N][N];

int getans(int t,int x,int y){
	memset(f,0,sizeof(f));
	f[0][0]=std::max(t-(x+y)+1,0ll);
	for(int i=0;i<=x;i++){
		for(int j=0;j<=y;j++){
			f[i+1][j]+=f[i][j]/2;
			f[i][j+1]+=f[i][j]-f[i][j]/2;
		}
	}
	return f[x][y];
}

signed main(){
	std::ios::sync_with_stdio(false);
	std::cin.tie(0);
	std::cout.tie(0);
	int T;
	std::cin>>T;
	while(T--){
		int t,x,y;
		std::cin>>t>>x>>y;
		if(getans(t,x,y)-getans(t-1,x,y)>0) std::cout<<"YES"<<'\n';
		else std::cout<<"NO"<<'\n';
	}
	return 0;
}

os:思维题,感觉确实还好,但是赛时并做不到这里hhhh

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值