Codeforces Round #585 (Div. 2) A-E

A:

贪心分配黄牌即可

#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
//typedef __int128 LL;
//typedef unsigned long long ull;
//#define F first
//#define S second
typedef long double ld;
typedef pair<int,int> pii;
typedef pair<ll,ll> pll;
typedef pair<ld,ld> pdd;
const ld PI=acos(-1);
const ld eps=1e-9;
//unordered_map<int,int>mp;
#define ls (o<<1)
#define rs (o<<1|1)
#define pb push_back
//#define a(i,j) a[(i)*(m+2)+(j)]  //m是矩阵的列数
//pop_back()
const int seed=131;
const int M = 1e5+7;
/*
int head[M],cnt;
void init(){cnt=0,memset(head,0,sizeof(head));}
struct EDGE{int to,nxt,val;}ee[M*2];
void add(int x,int y,int z){ee[++cnt].nxt=head[x],ee[cnt].to=y,ee[cnt].val=z,head[x]=cnt;}
*/
 
int main()
{
	ios::sync_with_stdio(false);
  	cin.tie(0);
  	int a,b,k1,k2,n,nn;
  	cin>>a>>b>>k1>>k2>>n;
  	nn=n;
	if(k1>k2)swap(k1,k2),swap(a,b);
	int ni=a*(k1-1)+b*(k2-1);
	
	int w=n/k1,nm=0;
	if(w>=a){
		n-=a*k1;
		nm+=a;
		nm+=n/k2;
	}
	else nm+=n/k1;
	cout<<max(nn-ni,0)<<" "<<nm<<endl;
	return 0;
}

B:

我是DP写的,很好写

这题也可以思维去做,比较难想。。就是算出负区间的个数

#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
//typedef __int128 LL;
//typedef unsigned long long ull;
//#define F first
//#define S second
typedef long double ld;
typedef pair<int,int> pii;
typedef pair<ll,ll> pll;
typedef pair<ld,ld> pdd;
const ld PI=acos(-1);
const ld eps=1e-9;
//unordered_map<int,int>mp;
#define ls (o<<1)
#define rs (o<<1|1)
#define pb push_back
//#define a(i,j) a[(i)*(m+2)+(j)]  //m是矩阵的列数
//pop_back()
const int seed=131;
const int M = 2e5+7;
/*
int head[M],cnt;
void init(){cnt=0,memset(head,0,sizeof(head));}
struct EDGE{int to,nxt,val;}ee[M*2];
void add(int x,int y,int z){ee[++cnt].nxt=head[x],ee[cnt].to=y,ee[cnt].val=z,head[x]=cnt;}
*/
ll f[M][2];//0负数 1正数
int a[M]; 
int main()
{
	ios::sync_with_stdio(false);
  	cin.tie(0);
  	int n;
  	cin>>n;
  	for(int i=1;i<=n;i++)
  	cin>>a[i];
  	if(a[1]>0)f[1][1]=1;
  	else f[1][0]=1;
  	for(int i=2;i<=n;i++)
  	{
  		if(a[i]>0)
  		f[i][0]=f[i-1][0],f[i][1]=f[i-1][1]+1;
  		else
  		f[i][0]=f[i-1][1]+1,f[i][1]=f[i-1][0];
	}
	ll ans1=0,ans2=0;
	for(int i=1;i<=n;i++)ans1+=f[i][0],ans2+=f[i][1];
	cout<<ans1<<" "<<ans2<<endl;
	return 0;
}

C:

只有a、b个数和为偶数,且s、t不同字符数也要是偶数个

 

 

#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
//typedef __int128 LL;
//typedef unsigned long long ull;
//#define F first
//#define S second
typedef long double ld;
typedef pair<int,int> pii;
typedef pair<ll,ll> pll;
typedef pair<ld,ld> pdd;
const ld PI=acos(-1);
const ld eps=1e-9;
//unordered_map<int,int>mp;
#define ls (o<<1)
#define rs (o<<1|1)
#define pb push_back
//#define a(i,j) a[(i)*(m+2)+(j)]  //m是矩阵的列数
//pop_back()
const int seed=131;
const int M = 2e5+7;
/*
int head[M],cnt;
void init(){cnt=0,memset(head,0,sizeof(head));}
struct EDGE{int to,nxt,val;}ee[M*2];
void add(int x,int y,int z){ee[++cnt].nxt=head[x],ee[cnt].to=y,ee[cnt].val=z,head[x]=cnt;}
*/
char s[M],t[M];
int p1[M],p2[M];
int main()
{
	ios::sync_with_stdio(false);
  	cin.tie(0);
  	int n;
  	cin>>n;
  	cin>>s>>t;
  	int na=0,nb=0,s1=0,s2=0;
  	for(int i=0;i<n;i++){
  		if(s[i]==t[i])continue;
//  		if(s[i]=='a')na++;else nb++;
//  		if(t[i]=='a')na++;else nb+;
  		if(s[i]=='a')p1[++s1]=i+1;
  		else p2[++s2]=i+1;
	}
//	cout<<s1<<" "<<s2<<endl;
	if((s1+s2)&1){
		cout<<-1<<endl;
		return 0;
	}
	if(s1%2==0)
	{
		cout<<(s1+s2)/2<<endl;
		for(int i=1;i<=s1;i+=2)cout<<p1[i]<<" "<<p1[i+1]<<endl;
		for(int i=1;i<=s2;i+=2)cout<<p2[i]<<" "<<p2[i+1]<<endl;
		return 0;
	}
	{
		cout<<s1/2+s2/2+2<<endl;
		
		for(int i=2;i<=s1;i+=2)cout<<p1[i]<<" "<<p1[i-1]<<endl;
		for(int i=2;i<=s2;i+=2)cout<<p2[i]<<" "<<p2[i-1]<<endl;
		cout<<p1[s1]<<" "<<p1[s1]<<endl;
		cout<<p1[s1]<<" "<<p2[s2]<<endl;
	}
	return 0;
}

D:

这种博弈一般要么SG要么找规律

这题我们发现,先手肯定是想让数的差距尽可能大

判断一下让大的数变大,和让小的数变大能否大于另一个数(即后手无论怎么补救都补救不回来)

#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
//typedef __int128 LL;
//typedef unsigned long long ull;
//#define F first
//#define S second
typedef long double ld;
typedef pair<int,int> pii;
typedef pair<ll,ll> pll;
typedef pair<ld,ld> pdd;
const ld PI=acos(-1);
const ld eps=1e-9;
//unordered_map<int,int>mp;
#define ls (o<<1)
#define rs (o<<1|1)
#define pb push_back
//#define a(i,j) a[(i)*(m+2)+(j)]  //m是矩阵的列数
//pop_back()
const int seed=131;
const int M = 2e5+7;
/*
int head[M],cnt;
void init(){cnt=0,memset(head,0,sizeof(head));}
struct EDGE{int to,nxt,val;}ee[M*2];
void add(int x,int y,int z){ee[++cnt].nxt=head[x],ee[cnt].to=y,ee[cnt].val=z,head[x]=cnt;}
*/
char s[M];
int main()
{
	ios::sync_with_stdio(false);
  	cin.tie(0);
  	int n;
  	cin>>n;
  	cin>>s;
  	int nl=0,nr=0,al=0,ar=0;
  	for(int i=0;i<n;i++)
  	if(i<n/2){
  		if(s[i]=='?')nl++;
  		else al+=s[i]-'0';
	  }
	else{
		if(s[i]=='?')nr++;
  		else ar+=s[i]-'0';
	}
//先手选择让左边大或者右边大 
	int wl=nl/2*9+al+(nl&1)*9;
	int wr=nr/2*9+ar+(nr&1)*9;
	//cout<<wl<<" "<<wr<<endl;
	if(wl!=wr)//先手想让左边大 
	{
		cout<<"Monocarp"<<endl;
		return 0;
	}
	cout<<"Bicarp"<<endl;
	return 0;
}

E:

状压DP

我们发现,最终数如果顺序固定,最优的方案一定是先让左边的数排好,再排次左边的,,然后排完所有的。

一般问题我们可以从边界来考虑,这一题也是。假如最终数的顺序固定,最左边的数先排好肯定是最优,不会变差(因为无论你顺序是什么,总要把最后最左边的数字移到最左边 。比如2 3 3 4 3,我们想让最后的顺序是3 4 2  ,那么无论你怎么移动,先把3移到最左边总是没错的,且是最优的:3 3 3 2 4 ,然后再考虑其他的数)。后面的数又可以归类到前面的问题,同样先排好最左边的数。依次类推。

明显:我们可以枚举已经排序的数的状态。枚举最后一次排序的数,进行递推即可

 

因为交换两个数时不会改变其他数的相对位置。

所以我们可以记录状态,递推一下即可。

f[i] 表示排好当前的数,所需的最小代价。

排好i数的代价一定是:所有i数前面的其他数的个数(不包括已经排好顺序的数)

预处理出代价就可以状压DP了

我们的策略始终是把确定的数,从左往右排,所以递推时,枚举最后排的数,加上代价递推即可。

#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const int seed=131;
const int M = 4e5+7;
int ct[21];
int a[M];
ll cost[21][21];
const int N =(1<<20)+1;
ll f[N];//状态已经排好的数的状态,的最小交换次数,从左往右排 
int main()
{
	ios::sync_with_stdio(false);
  	cin.tie(0);
  	int n,ma=0;
  	cin>>n;
  	for(int i=1;i<=n;i++){
  		cin>>a[i];
		for(int j=1;j<=20;j++)cost[a[i]][j]+=ct[j];//a[i]前面 的a[j]的个数,类比逆序对 
		ct[a[i]]++;
		ma=max(a[i],ma);
	}
	memset(f,60,sizeof(f));
	f[0]=0;
	for(int i=0;i<=(1<<ma)-1;i++)//枚举状态 从左往右排到当前的状态 
  	{
		for(int j=1;j<=ma;j++)//枚举最后一个排好的数字
  		{
  			if(!(i>>(j-1)))continue; //当前状态不存在这个数字 
			ll tp=0;
			for(int k=1;k<=ma;k++)
			{
				if(i&(1<<(k-1)))continue;//已经排好的数字我们就不计算他的贡献了(因为他们已经在最左边了)
				tp+=cost[j][k];
			}
			f[i]=min(f[i],f[i^(1<<(j-1))]+tp);
		}
	}
	cout<<f[(1<<ma)-1]<<endl;
	return 0;
}

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值