Codeforces 867 div3 A-G2

A. TubeTube Feed

分析

直接遍历每个视频,遍历到第i个视频,他前面的所有视频都直接一秒钟划过去,然后看剩下的时间够不够看完第i个视频,如果够,则更新最大有趣值

C++代码:

#include<iostream>
using namespace std;
const int N=55;
int a[N],b[N];
void solve(){
	int n,t;
	cin>>n>>t;
	for(int i=1;i<=n;i++)cin>>a[i];
	for(int i=1;i<=n;i++)cin>>b[i];
	int maxx=0,k=0;
	for(int i=1;i<=n;i++){
		if(i-1+a[i]<=t){//前i-1个视频一秒划过,该视频需要a[i]的时间观看
			if(b[i]>maxx){
				maxx=b[i];
				k=i;
			}
		}
	}
	if(!maxx)cout<<-1<<'\n';
	else cout<<k<<'\n';
}
int main(){
	int t;
	cin>>t;
	while(t--){
		solve();
	}
	return 0;
} 

B. Karina and Array

分析

如果有正数有负数的话,则最大值要么是最大的两个正数相乘,要么是两个最小的负数相乘,因为数可以随便删,所以直接排序,取两端的两个数相乘的最大值

C++代码:

#include<iostream>
#include<algorithm>
using namespace std;
const int N=200010;
typedef long long LL;
int a[N];
void solve(){
	int n;
	cin>>n;
	for(int i=1;i<=n;i++)cin>>a[i];
	sort(a+1,a+n+1);
	LL t=(LL)a[1]*a[2];//最小的两个数相乘
	t=max(t,(LL)a[n]*a[n-1]);//最大的两个数相乘
	cout<<t<<'\n';
}
int main(){
	int t;
	cin>>t;
	while(t--){
		solve();
	}
	return 0;
}

C. Bun Lover

分析

f[4]=26

直接找规律,发现规格k(k>4)的长度为f[k]=f[k-1]+2*k-1

所以直接推公式计算每个规格的长度,最终算出公式为:n^2+2*n+2

C++代码:

#include<iostream>
using namespace std;
typedef long long LL;
void solve(){
	LL n;
	cin>>n;
	cout<<n*n+2*n+2<<'\n';
}
int main(){
	int t;
	cin>>t;
	while(t--){
		solve();
	}
	return 0;
}

D. Super-Permutation

分析

打表找规律,直接看样例猜也是可以的,会发现奇数个数除了1之外都不行,偶数就是让奇数在偶数位上递减,n放在第一位,其余偶数在奇数位递增即可

C++代码:

#include<iostream>
#include<vector>
using namespace std;
void solve(){
	int n;
	cin>>n;
	if(n==1)cout<<1<<'\n'; 
	else if(n%2)cout<<-1<<'\n';
	else{
		vector<int> ans;
		ans.push_back(n);
		int a=(n==2?0:2),b=n-1;
		for(int i=2;i<=n;i++)
			if(i%2)ans.push_back(a),a+=2;
			else ans.push_back(b),b-=2;
		for(auto t:ans)cout<<t<<" ";
		cout<<'\n';
		
	}
} 
int main(){
	int t;
	cin>>t;
	while(t--){
		solve();
	}
	return 0;
} 

E. Making Anti-Palindromes

分析

首先,奇数个字符一定不可以,因为最中间的那个字母无论如何都满足s[i]==s[n-i+1]

如果有偶数个字符,记录每个字符出现的个数

1、如果个数最多的字符超过了字符串长度的一半,则不可能完成任务

2、否则记录一共需要换位置的次数cnt和每个需要换位置的字符的个数

如果出现最多的字符需要换位置次数sum1小于等于cnt/2,则可以两两匹配交换,一共需要(cnt+1)/2次

否则出现最多的字符需要换位置次数大于总数的一半,则需要max1次

C++代码:

#include<iostream>
#include<map>
using namespace std;
void solve(){
	int n;
	cin>>n;
	string s;
	cin>>s;
	s=' '+s;
	if(n%2)cout<<-1<<'\n';
	else{
		map<char,int> mp,mp1;
		int cnt=0,maxx=0,max1=0;
		for(int i=1;i<=n;i++){
			mp[s[i]]++;
			maxx=max(maxx,mp[s[i]]);//maxx记录个数最多的字符的个数
			if(s[i]==s[n-i+1]&&i<=(n+1)/2)
				cnt++,mp1[s[i]]++,max1=max(max1,mp1[s[i]]);
		}
		if(maxx>n>>1)cout<<-1<<'\n';//maxx>n/2,则不可能
		else if(max1<=cnt/2)cout<<(cnt+1)/2<<'\n';
		else cout<<max1<<endl;
	}
}
int main(){
	int t;
	cin>>t;
	while(t--){
		solve();
	}
	return 0;
}

F. Gardening Friends

分析

换根DP

虽然这题很多题解都是用树的直径做,确实代码会更简单,但是这是我第一次赛时做出换根DP的题,还是写一下换根DP的做法

之前做过一个类似的,链接https://codeforces.com/contest/1881/problem/F,当时是第一次接触换根DP,所以今天遇到了类似的题格外兴奋hh,然后做出来了,成就感满满

第一次dfs1从1开始搜索离每个点到达其他点的最远距离d1和次远距离d2,顺便记录一下每个点的深度,默认1的深度为0

第二次dfs2利用换根DP,也是从1开始搜,计算时先不减去操作的花销c,将每个点到达其他点的最大距离都求出来,然后在统计答案的时候把每个点的d1减去c*该节点的深度

因为每个节点的深度就是距离节点1的层数,也就是需要换多少次根,所以直接减去dep[i]*c即可

C++代码:

#include<iostream>
#include<vector>
using namespace std;
const int N=200010;
#define int long long
vector<int> e[N];
int d1[N],d2[N],dep[N];//最大值,次大值,深度(默认1的深度为0)
int n,k,c;
void dfs1(int u,int fa){
	d1[u]=d2[u]=0;
	if(u>1)dep[u]=dep[fa]+1;
	for(auto t:e[u]){
		if(t==fa)continue;
		dfs1(t,u);
		int s=d1[t]+k;
		if(s>=d1[u])d2[u]=d1[u],d1[u]=s;
		else if(s>d2[u])d2[u]=s;
	}
}
void dfs2(int u,int fa){
	if(fa!=-1){
        //记录次大值在此刻需要用上了
		if(d1[fa]==d1[u]+k){//如果fa的最大距离就是由u走过去的,则不能用fa的最大距离更新u,只能用次大距离
			if(d2[fa]+k>=d1[u]){
				d2[u]=d1[u],d1[u]=d2[fa]+k;
			}else if(d2[fa]+k>d2[u])
				d2[u]=d2[fa]+k;
		}else{
			if(d1[fa]+k>=d1[u]){
				d2[u]=d1[u],d1[u]=d1[fa]+k;
			}else if(d1[fa]+k>d2[u])
				d2[u]=d1[fa]+k;
		}
	}
	for(auto t:e[u]){
		if(t==fa)continue;
		dfs2(t,u);
	}
}
void solve(){
	cin>>n>>k>>c;
	for(int i=1;i<=n;i++)e[i].clear();
	for(int i=1;i<n;i++){
		int a,b;
		cin>>a>>b;
		e[a].push_back(b);
		e[b].push_back(a); 
	}
	dfs1(1,-1);
	dfs2(1,-1);
	int ans=0;
	for(int i=1;i<=n;i++)ans=max(ans,d1[i]-c*dep[i]);
	cout<<ans<<endl;
}
signed main(){
	int t;
	cin>>t;
	while(t--){
		solve();
	}
	return 0;
}

G1 and G2. Magic Triples

分析

看题解才知道还有值域分治这玩意儿,学到了

x/b,x,x*b
值域分治

用个map记录a种每个数出现的次数,然后枚举每个数作为中间的数,枚举中间的数的因子判断两边的数是否存在,存在答案就加上mp[x]*mp[x/b]*mp[x*b]

还有一种特殊情况即三个数相同,显然只有一个数的个数大于等于3时才有这种情况,所以枚举的时候记得加上这个

分治如下:
1、x<1e6暴力枚举x的所有因子,时间复杂度O(n * √n) 即O(n*1000)

2、1e9>=x>=1e6也是枚举因子b,此时由于b*x<=max(a[1~n]),所以b<=1000,时间复杂度为O(n*1000) 

C++代码:

#include<iostream>
#include<map>
#define int long long
using namespace std;
const int N=200010;
int a[N];
int n;
void solve(){
	cin>>n;
	map<int,int> mp;
	for(int i=1;i<=n;i++)cin>>a[i],mp[a[i]]++;
	int ans=0;
	for(auto t:mp){
		int x=t.first,y=t.second;
		ans+=y*(y-1)*(y-2);//相同的三个数,如果y小于3,则一定会乘一个0
		if(x<1e6){
			for(int i=1;i*i<=x;i++){
				if(x%i==0){
					if(i!=1&&mp.count(x/i)&&mp.count(x*i))ans+=mp[x]*mp[x/i]*mp[x*i];
					int i1=x/i;
					if(i1!=i&&mp.count(x/i1)&&mp.count(x*i1))ans+=mp[x]*mp[x/i1]*mp[x*i1];
				}
			}
		}else{
			for(int i=1;i*x<=1e9;i++){
				if(x%i==0){
					if(i!=1&&mp.count(x/i)&&mp.count(x*i))ans+=mp[x]*mp[x/i]*mp[x*i];
					int i1=x/i;
					if(i1!=i&&mp.count(x/i1)&&mp.count(x*i1))ans+=mp[x]*mp[x/i1]*mp[x*i1];
				}
			}
		}
	}
	cout<<ans<<'\n';
} 
signed main(){
	ios::sync_with_stdio(false);
	cin.tie(0);
	cout.tie(0);
	int t;
	cin>>t;
	while(t--){
		solve();
	}
	return 0;
}

  • 8
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值