【codeforces】赛后总结 edu161

Posted on 2024-01-26 00:04  木易meow  阅读(22)  评论(0编辑  收藏  举报

开篇碎碎念

其实应该是总结才对,小号打的,赛时ABC三题,rank 3k+,rating change回滚了。
赛后补了下DE

A.Tricky Template

题意:

判断是否能构造出模板链使AB链匹配而C链不匹配,匹配规则:当模板为小写的时候,匹配链对应位置必须相同,当模板为大写时,匹配链必须不同,

解题思路:

因为是和C链不匹配所以特殊关注C链的匹配性,而且只要有一位无法和C链匹配上那么就和C链不匹配。所以容易判断出YES的情况。
当C链和AB链该位置不同时就可以选择模板链小写构造出符合要求的模板

code:

#include<bits/stdc++.h>
using namespace std;
#define int long long 
#define endl "\n"
const int MAXN=1e5+10;
void sol(){
	int n;
	cin>>n;
	string a,b,c;
	cin>>a>>b>>c;
	int f=0;
	for(int i=0;i<n;i++){
		if(a[i]!=c[i] && b[i]!=c[i]){
			f=1;
			break;
		}
	}
	if(f==1)cout<<"Yes"<<endl;
	else cout<<"No"<<endl;
}
signed main(){
	ios::sync_with_stdio(false);cin.tie(0);cout.tie(0);
	int t;
	cin>>t;
	while(t--){
		sol();
	}
	return 0;
}

B. Forming Triangles

题意:

有 n 根木棒,编号从 1 到 n 。第 i 根木棒的长度是 2^ai 。从给出的n个木棒中选择三根构成一个非退化三角形(面积绝对大于零)的数目。

解题思路

观察长度发现都是二的次幂,两个二的较低次幂的木棍的和是不大于任何一个较高次幂的。所以只可能构成等边或者两腰较长的等腰三角形。
所以存储每个长度的个数,然后进行选择。

code

#include<bits/stdc++.h>
using namespace std;
#define int long long 
#define endl "\n"
const int MAXN=3e5+10;
int a[MAXN];
int C3(int x){
	return x*(x-1)/2*(x-2)/3;
}
int C2(int x){
	return x*(x-1)/2;
}
void sol(){
	int n,k,ans=0,tot=0;
	cin>>n;
	for(int i=1;i<=n;i++){
		cin>>k;
		a[k]++;
	}
	for(int i=0;i<=n;i++){
		if(a[i]>=3){
			ans+=C3(a[i]);
			ans+=C2(a[i])*tot;
		}
		else if(a[i]==2){
			ans+=C2(a[i])*tot;
		}
		tot+=a[i];
		a[i]=0;
	}
	cout<<ans<<endl;
}
signed main(){
	ios::sync_with_stdio(false);cin.tie(0);cout.tie(0);
	int t;
	cin>>t;
	while(t--){
		sol();
	}
	return 0;
}

C. Closest Cities

题意:

数轴上有n个城市,分别位于ai(ai升序排列),两个城市的距离为abs(ai-aj)。每个城市到达其最近城市的代价为1,其余城市为距离。
q次询问,分别给出两个城市u.v,问从u到v的最小花费

解题思路

前缀和分别计算从小到大城市的花费和从大到小,两个前缀和数组。
对于前缀和的证明:由于每两个城市之间都是采用的最优策略,所以从a到b就是从1到b的代价减去1到(a-1)的代价
贪心对于每个城市来说他的最近城市一定是和他相邻的城市之一。能使用相邻城市的快速穿梭就使用快速穿梭。

code

#include<bits/stdc++.h>
using namespace std;
#define int long long 
#define endl "\n"
const int MAXN=1e5+10;
int a[MAXN],b[MAXN],c[MAXN];
void sol(){
	int n;
	cin>>n;
	for(int i=1;i<=n;i++){
		cin>>a[i];
	}
	a[0]=-1e16,a[n+1]=1e16;
	int ans=0;
	for(int i=1;i<n;i++){
		if(a[i+1]-a[i]>a[i]-a[i-1]){
			ans+=a[i+1]-a[i];
		}
		else ans+=1;
		b[i+1]=ans;
	}
	b[1]=0;
	ans=0;
	for(int i=n;i>1;i--){
		if(a[i]-a[i-1]>a[i+1]-a[i]){
			ans+=a[i]-a[i-1];
		}
		else ans+=1;
		c[i-1]=ans;
	}
	c[n]=0;
	int q,u,v;
	cin>>q;
	for(int i=1;i<=q;i++){
		cin>>u>>v;
		if(u>v){
			cout<<c[v]-c[u]<<endl;
		}
		else cout<<b[v]-b[u]<<endl;
	}
}
signed main(){
	ios::sync_with_stdio(false);cin.tie(0);cout.tie(0);
	int t;
	cin>>t;
	while(t--){
		sol();
	}
	return 0;
}

D. Berserk Monsters

题目意思

有n个怪物,每个怪物的攻击力为ai,防御力是di,每轮每个怪物分别攻击一次它左边的怪兽和一次右边的怪兽(如果存在的话),只有受到的攻击总和大于其防御力的时候会死,输出每一轮死亡的怪兽数目

解题思路

说实话我是为了这个题而记录的整场比赛。首先是一个链表的操作,方便删除死亡的怪兽进行新一轮的计算。但是每个死亡的怪兽不是一旦判定死亡就要更新左右关系,不然可能会存在本该死亡的怪兽,因为它旁边的怪兽也死了,计算的时候就没算到它旁边的怪兽对它的攻击,从而没有死亡。(好叭我是赛后查看数据的时候发现的这个问腿)
于是我增加了一个vector记录每一轮的死亡的怪兽分别是几号,在轮次结束的时候再进行死亡的更新操作。
然后TLE...
观察样例,有些怪兽至始至终都不会死,所以如果判断了多次那么肯定是浪费时间,只有有怪兽死亡才会有新的左右关系变化,所以新建一个vector存一下每次死亡的怪兽的相邻的怪兽
注意特判,在第一次前虽然没有怪兽死亡,但是每个怪兽都有可能死亡,所以都应该加入待判列表

code

#include<bits/stdc++.h>
using namespace std;
#define int long long
#define endl "\n"
const int MAXN=3e5+10;
struct monster{
	int a;
	int d;
	int l;
	int r;
}m[MAXN];
int n;
int vis[MAXN],dele[MAXN];
vector<int>del;
vector<int>qdel;
void add(int i){
	if(i<1 || i>n)return;
	if(!vis[i] && !dele[i]){
		vis[i]=1;
		qdel.push_back(i);
	}
}
void de(int i){
	m[m[i].l].r=m[i].r;
	m[m[i].r].l=m[i].l;
	add(m[i].l);
	add(m[i].r);
}
void sol(){
	cin>>n;
	for(int i=1;i<=n;i++){
		cin>>m[i].a;
		dele[i]=0;
	}
	for(int i=1;i<=n;i++){
		cin>>m[i].d;
	}
	m[n+1].a=0,m[0].a=0;
	for(int i=1;i<=n+1;i++){
		m[i].l=i-1;
		m[i-1].r=i;
	}
	for(int i=1;i<=n;i++){
		add(i);
	}
	for(int i=1;i<=n;i++){
		int tot=0;
		for(int j=0;j<qdel.size();j++){
			int k=qdel[j],ta;
			vis[k]=0;
			ta=m[m[k].l].a+m[m[k].r].a;
			if(ta>m[k].d){
				del.push_back(k);
				tot++;
			}
		}
		qdel.clear();
		for(int j=0;j<del.size();j++){
			dele[del[j]]=1;
		}
		for(int j=0;j<del.size();j++){
			de(del[j]);
		}
		del.clear();
		cout<<tot<<' ';
	}
	cout<<endl;
}
signed main(){
	ios::sync_with_stdio(false);cin.tie(0);cout.tie(0);
	int t;
	cin>>t;
	while(t--){
		sol();
	}
	return 0;
}

E. Increasing Subsequences

题意:

给出n,求出一个长度在200以内的,含有严格单调递增子序列个数为n的数列

解题思路:

因为一个数的和空串都算严格单调递增子序列。所以发现长度为k的严格单调数列含有的严格单调递增子序列个数为2^k,进而考虑到n的二进制表达

code

#include<bits/stdc++.h>
#define int long long
#define endl "\n"
using namespace std;
const int MAXN=3e5+10;
int a[MAXN];
void sol(){
	int n,x;
	cin>>n;
	x=n;
	int cnt=0;
	vector<int>ans;
	while(n){
		if(n&1){
			a[cnt++]=1;
		}
		else a[cnt++]=0;
		n>>=1;
	}
	cnt--;
	for(int i=0;i<cnt;i++){
		ans.push_back(i);
	}
	for(int i=cnt-1;i>=0;i--){
		if((x>>i)&1)ans.push_back(i);
	}
	cout<<ans.size()<<endl;
	for(auto i:ans)cout<<i<<' ';
	cout<<endl;
}
signed main(){
	ios::sync_with_stdio(false);cin.tie(0);cout.tie(0);
	int t;
	cin>>t;
	while(t--){
		sol();
	}
}