【CF #780 Div3】A-F2

A. Vasya and Coins

题目

分析

有一定数量的1元和2元的硬币,找出最小不能表示的金额。

直接输出答案,当没有1一元硬币的时候特判。

代码

#include<bits/stdc++.h>
#define ll long long
using namespace std;
const ll maxn=1500;
const ll mod=998244353;
const ll MAX=0x3f3f3f3f;
const double pi=acos(-1);

int main()
{
	int t;
	cin>>t;
	while(t--)
	{
		int a,b;
		cin>>a>>b;
		int sum=a+b*2+1;
		if(a==0) cout<<1<<endl;
		else 
		cout<<sum<<endl; 
	} 
	return 0;
} 

B. Vlad and Candies

题目

分析

n个数,每次选一个数-1,每次-1的数不能是同一个,判断这个序列是否可以这样操作。

只需判断最大的数是否能减掉就行了,所以看最大数和第二大数差值是否为1(这样最大数消掉别的数肯定也能一起消掉),特判n=1的情况。

但是之后和朋友讨论,最大数可以和好多个小数消掉,比如数据1 1 1 3,这应该是可以,但是用ac代码出来是NO,也不知道是题目没看明白还是题目问题,存疑!!

代码

#include<bits/stdc++.h>
#define ll long long
using namespace std;
const ll maxn=2e5+10;
const ll mod=998244353;
const ll MAX=0x3f3f3f3f;
const double pi=acos(-1);
int a[maxn];

int main()
{
	int t;
	cin>>t;
	while(t--)
	{
		int n;
		cin>>n;
		ll sum=0;
		int f=1;
		for(int i=1;i<=n;i++)
		{
			cin>>a[i];
		}
		sort(a+1,a+1+n);
		if(n==1&&a[1]!=1)
		{
			cout<<"NO"<<endl;
			continue;
		}
		if(a[n]-a[n-1]<2) cout<<"YES"<<endl;
		else cout<<"NO"<<endl;
	} 
	return 0;
} 

C. Get an Even String

题目

分析

给一个字符串,求最少删掉多少个字符,可以让每个字符成为偶数。

这里采用了map容器用来存字符数,然后模拟题意就行了。

代码

#include<bits/stdc++.h>
#define ll long long
using namespace std;
const ll maxn=2e5+10;
const ll mod=998244353;
const ll MAX=0x3f3f3f3f;
const double pi=acos(-1);

int main()
{
	int t;
	cin>>t;
	while(t--)
	{
		string s;
		int f;
		cin>>s;
		map<char,int> mp;
		int ans=0;
		for(int i=0;i<s.size();i++)
		{
			if(!mp[s[i]])
			{
				mp[s[i]]=1;
				continue;
			}
			else {
				ans+=mp.size();
				ans--;
				mp.clear();
			}
		}
		ans+=mp.size();
		cout<<ans<<endl;
	} 
	return 0;
} 

D. Maximum Product Strikes Back

题目

分析

给一个序列,从左删或者从右删,使这个序列的乘积最大。

题目说,最大数不超过2,所以乘积最大,就是哪个区间内2的个数最多,然后因为有负数,所以需要判断负数个数为奇数时,从那边删掉一个负数,对乘积影响较小,直接模拟。

代码

#include<bits/stdc++.h>
#define ll long long
#define pb push_back
#define mk make_pair
using namespace std;
const ll maxn=2e5+10;
const ll mod=998244353;
const ll INF=0x3f3f3f3f;
const double pi=acos(-1);
int a[maxn];
int n;
int sum,ansl,ansr;

int count(int l,int r)
{
	int cnt=0;
	for(int i=l;i<=r;i++)
	{
		if(a[i]<0) cnt++;
	}
	return cnt;
}

void count2(int l,int r)
{
	int cnt=0;
	for(int i=l;i<=r;i++)
	{
		if(abs(a[i])==2) cnt++;
	}
	if(cnt>sum)
	{
		sum=cnt;
		ansl=l-1;
		ansr=n-r;
	}
}

void cacul(int l,int r)
{
	int t=count(l,r);
	if(t%2==0)
	{
		count2(l,r);
	}
	else {
		int l1=l;
		while(a[l1]>=0) l1++;
		count2(l1+1,r);
		int r1=r;
		while(a[r1]>=0) r1--;
		count2(l,r1-1);
	}
}

int main()
{
	int t;
	cin>>t;
	while(t--)
	{
		cin>>n;
		for(int i=1;i<=n;i++)
		{
			cin>>a[i];
		}
		a[n+1]=0;
		ansl=n;
		ansr=0;
		sum=0;
		for(int i=1,j=1;i<=n+1;i++)
		{
			if(a[i]==0)
			{
				cacul(j,i-1);
				j=i+1;
			}
		}	
		cout<<ansl<<" "<<ansr<<endl;	
	}
	return 0;
} 

E. Matrix and Shifts

题目

分析

给一个矩阵,对这个矩阵可以进行上下左右的滑动,有一种操作可以对一个位置取反(0变1,1变0),问最少需要多少次,可以让这个矩阵变为单位矩阵(只有对角线为1)

上下左右滑动可以通过复制数组 G[i][j] =G[i+n][j]=G[i][j+n]=G[i+n][j+n] ,枚举矩阵的左上角,固定宽度和长度就可以首先模拟矩阵上下左右滑动的效果。

对于每个矩阵来说,代价就是,所有1的个数-对角线1的个数+对角线0的个数,预处理出所有1的个数,动态维护长度为n的对角线为1,为0的个数即可。

代码

#include<bits/stdc++.h>
#define ll long long
#define pb push_back
#define mk make_pair
using namespace std;
const ll maxn=2e5+10;
const ll mod=998244353;
const ll INF=0x3f3f3f3f;
const double pi=acos(-1);
int mp[5100][5100];
int col[2010],row[2010];

int main()
{
	int t;
	cin>>t;
	while(t--)
	{
		int n;
		cin>>n;
		int cnt=0;
		for(int i=1;i<=n;i++)
		{
			for(int j=1;j<=n;j++)
			{
				char tt;
				cin>>tt;
				if(tt=='1') 
				{
					cnt++;
					mp[i][j]=mp[i+n][j]=mp[i][j+n]=mp[i+n][j+n]=1;
				}
				else mp[i][j]=mp[i+n][j]=mp[i][j+n]=mp[i+n][j+n]=0;
			}
		}
		int ans=INF;
		for(int i=1;i<=n;i++)
		{
			int cnt1=0,cnt0=0;
			for(int j=0;i+j<=2*n&&1+j<=2*n;j++) 
			{
				if(mp[i+j][1+j]==1) cnt1++;
				else cnt0++;
				if(j>=n-1)
				{
					ans=min(cnt-cnt1+cnt0,ans);
					if(mp[i+j-n+1][1+j-n+1]==1) cnt1--;
					else cnt0++;
				}
			}
		} 
		for(int i=1;i<=n;i++)
		{
			int cnt1=0,cnt0=0;
			for(int j=0;1+j<=2*n&&i+j<=2*n;j++)
			{
				if(mp[1+j][i+j]==1) cnt1++;
				else cnt0++;
				if(j>=n-1)
				{
					ans=min(cnt-cnt1+cnt0,ans);
					if(mp[1+j-n+1][i+j-n+1]==1) cnt1--;
					else cnt0++;
				}
			}
		}
		cout<<ans<<endl;
	}
	return 0;
} 

F1. Promising String (easy version)

题目

分析

F1和F2题意一样,区别为F1的n为1≤n≤3000,F2为1≤n≤2e5,给你只含“+”,“-”的字符串,你可以把相邻两个“-”转化为“+” , 问有多少个子字符串可以通过这种操作使“+”数量等于“-”,例如“±”,“±–”,都是合法的

前缀和处理出-,+的差,暴力枚举即可。

代码

#include<bits/stdc++.h>
#define ll long long
#define pb push_back
#define mk make_pair
using namespace std;
const ll maxn=2e5+10;
const ll mod=998244353;
const ll INF=0x3f3f3f3f;
const double pi=acos(-1);

int main()
{
	int t;
	cin>>t;
	while(t--)
	{
		int n;
		string s;
		cin>>n>>s;
		int ans=0;
		for(int i=0;i<s.size();i++)
		{
			int cnt=0;
			for(int j=i;j>=0;j--)
			{
				if(s[j]=='+') cnt--;
				else cnt++;
				if(cnt>=0&&cnt%3==0) ans++;
			}
		}
		cout<<ans<<endl;
	}
	return 0;
} 

 F2. Promising String (hard version)

题目

分析

显然不能暴力枚举子字符串 , 我们来看关键的判断条件s[ j ] >= s[ i ] 和 (sum[j]-sum[i])%3 == 0, 其中后者可以转化成 sum[i] ==sum[j] (在模3的意义下)
那么做法就是: 枚举每个子字符串的终点,统计出前面sum[j] >= sum[ i ] 同时 sum[j]%3 ==sum[i] %3的数量就好,那么对于每个点 i 我们可以用三个树状数组分别标记一下sum[i] ,要查询贡献度时在相应的树状数组中用前缀和相加就可以得到符合要求起点的数量。

例如{1,2,3,4} 对于sum[0] = 1, 模3也为1 ,那么在第二个树状数组中(第一个是记录模数为0的)记录一下sum[0]已经出现过,在遍历到sum[3] = 4,模数也为1,查询第二个树状数组中1~sum[4]的前缀和,就是这个点的贡献值;

但是由于sum数组会有负数,所以最后加上一个偏移量,让所有数大于0,不影响sum[j]-sum[i])%3 == 0的判断

代码

#include<bits/stdc++.h>
#define ll long long
using namespace std;
const ll maxn=2e6+10;
const ll mod=998244353;
const ll MAX=0x3f3f3f3f;
const double pi=acos(-1);
int n;
int tr[maxn][3];
int sum[maxn];
char s[maxn];
 
int lowbit(int x)
{
    return x &(-x);
}

void update(int x,int y,int m)  //单点更新
{
    for(int i=x;i<=maxn;i+=lowbit(i))    //x为更新的位置,y为更新后的数,n为数组最大值
        tr[i][m]+=y;
}

ll getsum(int x,int m)   //区间查询
{
    ll ans = 0;
    for(int i=x;i>=1;i-=lowbit(i))
        ans += tr[i][m];
    return ans;
}

int main()
{
	int t;
	cin>>t;
	while(t--)
	{
		cin>>n;
		cin>>(s+1);
		int minn=0;
		sum[0]=0;
		for(int i=1;i<=n;i++)
		{
			//sum[i]=sum[i-1]+(s[i]=='+'?-1:1);
			if(s[i]=='+')
			{
				sum[i]=sum[i-1]-1;
			}
			else sum[i]=sum[i-1]+1;
			minn=min(minn,sum[i]);
		}
		for(int i=0;i<=n-minn+10;i++)
		{
			tr[i][0]=tr[i][1]=tr[i][2]=0;
		}
		ll ans=0;
		for(int i=0;i<=n;i++)
		{
			sum[i]-=minn-1;
		}
		for(int i=0;i<=n;i++)
		{
			int tt=sum[i]%3;
			ans+=getsum(sum[i],tt);
			update(sum[i],1,tt);
		}
		cout<<ans<<endl;
	} 
	return 0;
}  

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值