Codeforces Round 962 (Div. 3)

A.贪心:https://codeforces.com/contest/1996/problem/A

AC代码:

#include<bits/stdc++.h>
using namespace std;
int t,n;
int main(){
	cin>>t;
	while(t--){
		cin>>n;
		int k=n/4;
		if(4*k==n) cout<<k<<endl;
		else{
			cout<<k+(n-4*k)/2<<endl;
		}
	}
}

B.枚举:https://codeforces.com/contest/1996/problem/B

AC代码:

#include<bits/stdc++.h>
using namespace std;
int t;
int a[1010][1010],n,k;
int main(){
	cin>>t;
	while(t--){
		cin>>n>>k;
		for(int i=1;i<=n;i++){
			for(int j=1;j<=n;j++){
				char c;
				cin>>c;
				a[i][j]=(int)(c-'0');
			}
		}
		for(int i=1;i<=n;i+=k){
			for(int j=1;j<=n;j+=k){
				cout<<a[i][j];
			}
			cout<<endl;
		}
	}
}

C.前缀和:https://codeforces.com/contest/1996/problem/C

问题其实就是:从两个串中截取同一区间,问有多少个字母不一样。

sum[i][j]为前i个字母中有多少个j的字母,假设求的区间是[l,r].

答案就是\sum_{i=a}^{i=z}abs( sum1[r][i]-sum1[l-1][i]-sum2[r][i]+sum2[l-1][i])/2

AC代码:

#include<bits/stdc++.h>
using namespace std;
int t;
char a[200010],b[200010];
int cun[30];
int sum1[200010][28];
int sum2[200010][28];
int n,q;
void init(){
	for(int i=1;i<=n;i++){
		for(int j=0;j<=26;j++){
			if(a[i]==(char)('a'+j)) sum1[i][j]=sum1[i-1][j]+1;
			else sum1[i][j]=sum1[i-1][j];
		}
	}
	for(int i=1;i<=n;i++){
		for(int j=0;j<=26;j++){
			if(b[i]==(char)('a'+j)) sum2[i][j]=sum2[i-1][j]+1;
			else sum2[i][j]=sum2[i-1][j];
		}
	}
}
int main(){
	cin>>t;
	while(t--){
		cin>>n>>q;
		for(int i=1;i<=n;i++){
			char kk;
			cin>>kk;
			a[i]=kk;
		}
		for(int i=1;i<=n;i++){
			char kk;
			cin>>kk;
			b[i]=kk;
		}
		init();
		while(q--){
			int l,r;
			scanf("%d%d",&l,&r);
			int ans=0;
			for(int j=0;j<=26;j++){
				ans+=abs(sum1[r][j]-sum1[l-1][j]-(sum2[r][j]-sum2[l-1][j]));
			}
			printf("%d\n",ans/2);
		}
		
	}
}

D.枚举:https://codeforces.com/contest/1996/problem/D

考虑到轮换性,不妨令a\leqslant b\leqslant c

因此:a*a*3\leqslant n,于是先枚举  a

a*b+a*c+b*c\geqslant b*b

于是我们再枚举b,这样c的可行解的范围也出来了:

[b,min(x-a-b,(n-a*b)/(a+b))]

AC代码:

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
int t,n,x;
ll cnt;
void solve(){
	for(int a=1;a*a*3<=n;a++){
		for(int b=a;b*b<=n;b++){
			if(a+b>=x) break;
			if(a*b+a+b>n) break;
			int fk=(n-a*b)/(a+b);
			int ck=min(fk,x-a-b);
			if(ck<b) continue;
			
			//计算贡献
			if(a==b){
				cnt++;//a=b=c
				cnt+=(ck-b)*3; 
			} 
			else{
				cnt+=3;
				cnt+=(ck-b)*6;
			}
		}
	}
}
int main(){
	cin>>t;
	while(t--){
		cin>>n>>x;
		cnt=0;
		solve();
		cout<<cnt<<endl;
	}
}

E递推:https://codeforces.com/contest/1996/problem/E

我们把0当成-1,1当成1,合法的区间也就是其值和为0,于是我们先处理出来前缀和sum

首先,我们固定y,看看他可以带来的贡献:

我们不妨让:sum[j]=sum[y] (j<y).这里的j是相比其他距离y最近的

显然当在[j,y]区间内x只可以落在j+1的位置上。

此时l可以落在[0,j+1]的任何位置,而r可以落在[y,n-1]的任意地方。

于是对答案贡献(n-y)*(j+2).

至于x落在[0,j-1]上的贡献,就和求j时的贡献一样(但是要*(j+2)),于是问题就是一个递推的过程。

我们记mp[j]为y落在j时的贡献(算贡献只考虑左边,毕竟现在右边不一样了),于是sum[y]=(n-y)*(j+2)+mp[j]*(n-y)

我们提前一步维护mp,这样每次跟新mp只要+(j+2)即可,不然还要维护一个数组存j的位置。

AC代码:

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
string s;
int t;
ll res=0;
ll mod=1e9+7;
int main(){
	cin>>t;
	while(t--){
		map<ll,ll> mp;
		res=0;
		cin>>s;
		ll cur=0;
		mp[cur]=1;
		int n=s.size();
		for(int i=0;i<=n-1;i++){
			cur+=(s[i]=='1'?1:-1);
			res=(res+(n-i)*mp[cur])%mod;
			mp[cur]=(mp[cur]+i+2)%mod;
		}
		cout<<res<<endl;
	}
}

F二分+优先队列:https://codeforces.com/contest/1996/problem/F

假如k不大,显然我们直接优先队列每次取a中最大的即可(k次里不取那不白白浪费了)。

但是这里的k是1e9的级别就不可以这么干了。

事实上,这个a数组的元素中的最大值一定是不严格递减的,最后一定会小于某一个值。

于是我们令f(x)表示所有值刚好小于等于x时用的最少次数(f(x)的实现就是枚举加和每一个(a[i]-x)/b[i]),他显然是单调的,我们得到f(x)\leqslant k的最小的x。

此时我们让k减掉x,可以发现此时的k一定小于等于n(假如大于n,我们对每一个a[i]再减去他们的b[i],这样就可以得到一个更小的x),于是我们就可以快乐地直接求了。

对于前面那部分对答案的贡献,因为每一个a[i]都是减到<=x就停止,我们用等差数列公式即可。

具体可以见下面的AC代码:

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
ll t,n,k,a[200010],b[200010];

bool check(ll x){
	ll cnt=0;
	for(int i=1;i<=n;i++){
		if(a[i]<=x) continue;
		if((a[i]-x)%b[i]==0) cnt+=(a[i]-x)/b[i];
		else cnt+=(a[i]-x)/b[i]+1;
	}
	if(cnt>k) return 0;
	return 1;
}
int main(){
	cin>>t;
	while(t--){
		cin>>n>>k;
		ll ans=0;
		for(int i=1;i<=n;i++) cin>>a[i];
		for(int i=1;i<=n;i++) cin>>b[i];
		ll l=0,r=1e9;
		while(l<r){
			int mid=(l+r)/2;
			if(check(mid)==1) r=mid;
			else l=mid+1;
		}
		ll x=l;
		//特判x==0 
		//计算贡献
		for(int i=1;i<=n;i++){
			if(a[i]<=x) continue;
			int k1;
			if((a[i]-x)%b[i]==0){
				k1=(a[i]-x)/b[i];
				ans+=(k1)*(a[i]+a[i]-b[i]*(k1-1))/2; 
			}
			else{
				k1=(a[i]-x)/b[i]+1;
				ans+=(k1)*(a[i]+a[i]-b[i]*(k1-1))/2;
			}
			ll hh=0;
			a[i]=max(hh,a[i]-b[i]*k1);
			k-=k1;
		}
		if(x==0){
			cout<<ans<<endl;
			continue;
		}
		priority_queue<pair<int,int> > q;
		for(int i=1;i<=n;i++) q.push({a[i],i});
		for(int i=1;i<=k;i++){
			pair<int,int> ck=q.top();
			q.pop();
			ans+=ck.first;
			q.push({ck.first-b[ck.second],ck.second});
		}
		cout<<ans<<endl;
	}
}

  • 7
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
Codeforces Round 894 (Div. 3) 是一个Codeforces举办的比赛,是第894轮的Div. 3级别比赛。它包含了一系列题目,其中包括题目E. Kolya and Movie Theatre。 根据题目描述,E. Kolya and Movie Theatre问题要求我们给定两个字符串,通过三种操作来让字符串a等于字符串b。这三种操作分别为:交换a中相同位置的字符、交换a中对称位置的字符、交换b中对称位置的字符。我们需要先进行一次预处理,替换a中的字符,然后进行上述三种操作,最终得到a等于b的结果。我们需要计算预处理操作的次数。 根据引用的讨论,当且仅当b[i]==b[n-i-1]时,如果a[i]!=a[n-i-1],需要进行一次操作;否则不需要操作。所以我们可以遍历字符串b的前半部分,判断对应位置的字符是否与后半部分对称,并统计需要进行操作的次数。 以上就是Codeforces Round 894 (Div. 3)的简要说明和题目E. Kolya and Movie Theatre的要求。<span class="em">1</span><span class="em">2</span><span class="em">3</span> #### 引用[.reference_title] - *1* *2* [Codeforces Round #498 (Div. 3) (A+B+C+D+E+F)](https://blog.csdn.net/qq_46030630/article/details/108804114)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v93^chatsearchT3_2"}}] [.reference_item style="max-width: 50%"] - *3* [Codeforces Round 894 (Div. 3)A~E题解](https://blog.csdn.net/gyeolhada/article/details/132491891)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v93^chatsearchT3_2"}}] [.reference_item style="max-width: 50%"] [ .reference_list ]
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值