Codeforces Round 918 (Div. 4)

A. Odd One Out

题目链接

题目大意

输入三个数,保证有两个数相同,输出那个不同的数

题解

第一次打div4,没想到签到题这么签到0v0

_______________________________________

可以用异或的性质来做,异或有以下两个性质:

1.两个相同的数异或等于0

2.0异或一个数等于这个数本身

发现这两个性质完美契合这道题,那我们直接输出三个数异或后的结果即可

代码

#include<iostream>
#include<algorithm>
#include<cstring>
#include<cmath>
//#include<queue>
//#include<map>
//#include<vector>
//#include<set>
//#include<deque>
//#include<bitset>
//#include<functional>

#define ll long long
#define inf 0x3f3f3f3f
using namespace std;

typedef pair<int,int>PII;

	
	
int main()
{
	int t;cin>>t;
	while(t--)
	{
		int a,b,c;
		cin>>a>>b>>c;
		int s=a^b^c;
		cout<<s<<endl;
	}
	
	
	return 0;
}

B. Not Quite Latin Square

题目链接

题目大意

给出一个3x3的矩阵,保证每行每列都由A,B,C构成,现在将其中一个元素替换为?,输出被替换为?的原字母

题解

我们遍历每行找到被替换为?的字母在哪一行,然后看这一行中A,B,C哪一个字母没出现,输出即可,这里用了map记录每个字母是否出现过

代码

#include<iostream>
#include<algorithm>
#include<cstring>
#include<cmath>
//#include<queue>
#include<map>
//#include<vector>
//#include<set>
//#include<deque>
//#include<bitset>
//#include<functional>

#define ll long long
#define inf 0x3f3f3f3f
using namespace std;

typedef pair<int,int>PII;

char s[4][4];
map<char,bool>mp;	
int k;
int main()
{
	int t;cin>>t;
	while(t--)
	{
		mp.clear();
		for(int i=1;i<=3;i++) scanf("%s",s[i]+1);//按行读图
		for(int i=1;i<=3;i++)
		{
			for(int j=1;j<=3;j++)
			{
				if(s[i][j]=='?')
				{
					k=i;
					break;
				}
			}
		}
			for(int i=1;i<=3;i++)
			{
				mp[s[k][i]]=true;
			}
			for(char c='A';c<='C';c++)
			{
				if(!mp[c]) cout<<c<<endl;
			}
		
	}
	
	
	return 0;
}

C. Can I Square?

题目链接

题目大意

现在有n个木桶,每个桶里有若干个边长为1的正方形,能否用所有的正方形组成一个大的正方形,能输出yes,不能输出no

题解

如果想用若干个边长为1的正方形组成一个大正方形,那么这个大正方形的面积和一定是个完全平方数,因为边长为1,所以我们只需判断小正方形总数是不是完全平方数即可

ps:这里使用对一个开平方后在相乘判断是否为完全平方数,但一定要注意sqrt的精度问题,因为第一次没考虑到精度问题导致吃了一发罚时,我们可以使用sqrtl开平方,或者在sqrt时使用floor向下取整

代码

#include<iostream>
#include<algorithm>
#include<cstring>
#include<cmath>
//#include<queue>
//#include<map>
//#include<vector>
//#include<set>
//#include<deque>
//#include<bitset>
//#include<functional>

#define ll long long
#define inf 0x3f3f3f3f
using namespace std;

typedef pair<int,int>PII;
const int N=2e5+10;


	
int main()
{
	int t;cin>>t;
	while(t--)
	{
		ll n;cin>>n;
		ll sum=0;
		for(ll i=1,x;i<=n;i++) 
		{
			cin>>x;
			sum+=x;
		}
		if(floor(sqrt(sum))*floor(sqrt(sum))==sum) puts("YES");
		else puts("NO");
		
	}
	
	
	
	return 0;
}

D. Unnatural Language Processing

题目链接

题目大意

卢拉觉得很无聊,决定用 a 、 b 、 c 、 d 和 e 这五个字母组成一种简单的语言。有两种类型的字母:

-元音-字母 a 和 e 。它们由 V 表示。

-辅音-字母 b 、 c 和 d 。它们由 C 表示。

语言中有两种类型的音节: CV (常量后接元音)或 CVC (元音前后带辅音)。

例如, ba 、 ced 、 bab 是音节,但 aa 、 eda 、 baba 不是音节。

语言中的单词是一系列音节。

卢拉用这种语言写了一个单词,但她不知道如何把它拆分成音节。帮她把这个单词分解成音节。例如,给定单词 bacedbab ,它将被拆分为音节 ba.ced.babba.ced.bab (点 .. 表示音节边界)

题解

我们可以发现每个音节只有两种情况,辅元 或者 辅元辅,为表示方便,我们用0代表元音,1代表辅音。那么两种音节分别为10和101,由于题目保证一定可以拆分成音节,那我们只需要将原串划分为10和101,在其之间加'.'即可。可以发现每个音节都由1开头,但是我们遇到1之后该将其之后的一个字母划分在一起还是将其之后的两个字母划分在一起呢,显然还需要进一步的判断来实现。但如果我们从后向前判断,每个音节就变成了01和101,那么我们遇到0向前划分一位,遇到1向前划分两位即可。将划分后的音节存入数组中,最后倒序输出答案即可

代码

#include<iostream>
#include<algorithm>
#include<cstring>
#include<cmath>
//#include<queue>
#include<map>
#include<vector>
//#include<set>
//#include<deque>
//#include<bitset>
//#include<functional>

#define ll long long
#define inf 0x3f3f3f3f
using namespace std;

typedef pair<int,int>PII;

map<char,int>mp;
	


int main()
{
	int t;cin>>t;
	while(t--)
	{
		mp['a']=mp['e']=0;
        mp['b']=mp['c']=mp['d']=1;	
        vector<char>res;
		string s;
		int n;cin>>n;
		cin>>s;
		for(int i=n-1;i>=0;i--)
		{
			if(mp[s[i]]==1) 
			{
				res.push_back({s[i]});
				res.push_back({s[i-1]});
				res.push_back({s[i-2]});
				res.push_back({'.'});
				i-=2;
			}
			else
			{
				res.push_back({s[i]});
				res.push_back({s[i-1]});
				res.push_back({'.'});
				i--;
			}
		}
		
		//for(int i=0;i<res.size();i++) cout<<res[i];
		for(int i=res.size()-2;i>=0;i--)//最后一个元素为.所以从倒数第二个元素输出
		{
			if(res[i]!='.') cout<<res[i];
			else cout<<'.';
		}
		cout<<endl;
	}
	
	
	return 0;
}

E. Romantic Glasses

题目链接

题目大意

给定一个数组,判断是否存在一个区间使得其中所有奇数下标元素之和等于偶数下标元素之和

题解

由于数据范围达到了2e5,枚举肯定会超时的,毕竟也是个e题。对于区间求和问题,我们首先能想到的就是前缀和。在这里先引入一个判断是否存在和为0区间的问题,我们都熟悉用前缀数组s表示j到i的区间和为s[j]-s[i-1],如果区间和为0,则s[j]-s[i-1]=0,变形一下即s[j]=s[i-1],也就是说只要前缀和数组中存在两个相同的前缀和,那么就一定有和为0的区间。回到这道题,我们可以与0区间问题联系在一起,由于所求区间奇数和等于偶数和,那么如果我们对其中一方取相反数,他们的和就变成了0,这样问题又转换成了求0区间问题。

但是有一个特殊情况(也是卡了我好久的地方,明明只差一步!),比如 1 3 2,那么取反之后为-1 3 -2,其前缀和为 -1 2 0,其前缀和数组中并不存在两个相同的前缀和,但是区间[1,3]之和为-1+3+-2=0,明显存在和为0的区间,此时为表达式为s[3]-s[1-1],而s[0]=0,所以0其实是一个隐藏的点,需要提前记录前缀0出现过。

代码

#include<iostream>
#include<algorithm>
#include<cstring>
#include<cmath>
//#include<queue>
#include<map>
//#include<vector>
#include<unordered_set>
#include<set>
//#include<deque>
//#include<bitset>
//#include<functional>

#define ll long long
#define inf 0x3f3f3f3f
using namespace std;

typedef pair<int,int>PII;
const int N=2e5+10;

ll f[N];	
ll s[N];

int n;
int main()
{
	int t;cin>>t;
	while(t--)
	{
		cin>>n;
		int flag=0;
		set<ll>e;
		e.insert(0);
		for(int i=1;i<=n;i++)
		{
			cin>>f[i];
			if(i%2==1) f[i]*=-1;
		}
		
		for(int i=1;i<=n;i++)
		{
			s[i]=s[i-1]+f[i];
			if(e.count(s[i]))
			{
				flag=1;
				break;
			}
			e.insert(s[i]);
		}
		
		if(flag==1) puts("YES");
		else puts("NO");
		
		
	}
	
	
	return 0;
}

F. Greetings

题目链接

题目大意

数轴上有 n个人,第 i 个人在点 ai ,想去点 bi 。对于每个人, ai<bi,所有人的起点和终点都是不同的。所有人将以每秒 1 个单位的速度同时开始移动,直到他们到达终点 bi。

当两个人在同一个点相遇时,他们会互相问候一次。会有多少问候?请注意,一个人仍然可以问候其他人,即使他们已经到达他们的终点。

题解

其实可以发现,只有一个区间在另一个区间内,才会产生贡献,因为所有人同时开始运动,其距离是不变的,唯一相遇打招呼的情况便是a到达终点后,b经过a终点。那么问题就变成了判断有多少个区间是另一区间的子区间,为了方便判断,可以将起点升序排列,看有几个区间的终点大于当前区间,

举一组例子 

26
39
45
18
710
-2100

将其按起点升序排列后为

-2100
18
26
39
45
710

(-2,100)区间的子区间(起点大于-2,终点小于100)个数为5个,(1,8)区间的子区间个数为2个,(2,6)区间的子区间个数为1个,(3,9)子区间个数为1个,(4,5)子区间个数为0个,(7,10)子区间个数为0个,所以答案为5+2+1+1+0+0=9,可以看出对于起点已经排序的情况下,我们只要看当前终点后面有几个数比其小,那么其就有几个子区间,即满足i<j,a[i]>a[j]的元素个数,很明显的逆序对,那么这题就变成了对终点求逆序对个数,对它使用逆序对板子即可

但是用之前学的归并逆序对模板跑到第三组就tle了,于是又学了一种递归的归并逆序对板子

tle的板子

#include<iostream>
#include<algorithm>
#include<cstring>
#include<cmath>
//#include<queue>
//#include<map>
//#include<vector>
//#include<set>
//#include<deque>
//#include<bitset>
//#include<functional>
 
#define ll long long
#define inf 0x3f3f3f3f
using namespace std;
 
typedef pair<int,int>PII;
const int N=2e5+10;
struct edge{
	ll a,b;
	bool operator < (const edge &t)const
	{
		return a<t.a;
	}
}f[N];
 
ll e[N];	
ll res[N];
ll ans;
void msort(ll l,ll r)
{
	if(l>=r) return;
	ll mid=l+r>>1;
	msort(l,mid);
	msort(mid+1,r);
	ll i=l,j=mid+1,k=l;
	while(i<=mid && j<=r)
	{
		if(e[i]<=e[j]) res[k++]=e[i++];
		else
		{
			res[k++]=e[j++];
			ans+=mid-i+1;
		}
	}
	while(i<=mid) res[k++]=e[i++];
	while(j<=r) res[k++]=e[j++];
	for(int i=1;i<=r;i++) e[i]=res[i];
	
	
}	
	
int main()
{
	int t;cin>>t;
	while(t--)
	{
		int n;cin>>n;
		ans=0;
		for(int i=1;i<=n;i++) cin>>f[i].a>>f[i].b;
		sort(f+1,f+n+1);
		for(int i=1;i<=n;i++) e[i]=f[i].b;
	    msort(1,n);
	    cout<<ans<<endl;
	}
	
	
	return 0;
}

正解代码如下

代码

#include<iostream>
#include<algorithm>
#include<cstring>
#include<cmath>
//#include<queue>
//#include<map>
//#include<vector>
//#include<set>
//#include<deque>
//#include<bitset>
//#include<functional>
 
#define ll long long
#define inf 0x3f3f3f3f
using namespace std;
 
typedef pair<int,int>PII;
const int N=2e5+10;
struct edge{
	int a,b;
	bool operator < (const edge &t)const
	{
		return a<t.a;
	}
}f[N];
 
int e[N];	
int b[N];


ll msort(int l1,int r1,int l2,int r2)
{
	int i=l1,j=l2,k=l1;
	ll ans=0;
	while(i<=r1 || j<=r2)
	{
		if(i==r1+1)
		{
			b[k++]=e[j++];
			continue;
		}
		if(j==r2+1)
		{
			b[k++]=e[i++];
			continue;
		}
		if(e[i]<=e[j]) b[k++]=e[i++];
		else
		{
			ans+=r1-i+1;
			b[k++]=e[j++];
		}
	}
	for(int i=l1;i<=r2;i++) e[i]=b[i];
	return ans;
}

ll dfs(int l,int r)
{
	if(l>=r) return 0;
	int mid=(l+r)/2;
	ll ans=0;
	ans+=dfs(l,mid);
	ans+=dfs(mid+1,r);
	ans+=msort(l,mid,mid+1,r);
	return ans;
}
	
int main()
{
	int t;cin>>t;
	while(t--)
	{
		int n;cin>>n;
		for(int i=1;i<=n;i++) cin>>f[i].a>>f[i].b;
		sort(f+1,f+n+1);
		for(int i=1;i<=n;i++) e[i]=f[i].b;
		cout<<dfs(1,n)<<endl;
	}
	
	
	return 0;
}

G. Bicycles

题目链接

题目大意

斯拉维克的所有朋友都计划从他们住的地方出发,骑自行车去参加一个聚会。除了斯拉夫人,他们都有自行车。他们可以通过 n个城市旅行。他们都住在城市 1 ,想参加位于城市 n 的聚会。城市地图可以看作是具有 n个节点和 m条边的无向图。边 i 连接城市 ui 和 vi ,长度为 wi 。斯拉维克没有自行车,但他有钱。每个城市只有一辆自行车出售。每辆自行车的减速系数为 si 。

一旦斯拉夫买了一辆自行车,他就可以随时使用它从他目前所在的城市到任何邻近的城市,只需花费 wi⋅sj时间。考虑到他正在使用自己的自行车 j 穿越Edge i 。斯拉夫人想买多少自行车就可以买多少,因为钱对他来说不是问题。

由于斯拉夫讨厌骑自行车旅行,他想在尽可能短的时间内从他的地方到达聚会。而且,由于他的信息学技能相当生疏,他向你寻求帮助。斯拉夫人从城市 11旅行到城市 n 所需的最短时间是多少?斯拉夫人没有自行车就不能旅行。

它保证斯拉夫人有可能从城市 1 旅行到任何其他城市。

题解

考虑到所用时间与每个城市的自行车速度有关,则有可能先到一个自行车速度最高(减速系数最低)的城市购买该城市的自行车后,再前往终点。先用最短路算法求出每个点(按自行车速度快慢排序)到终点的最短距离,然后在直接骑到终点的耗时和骑到该点换车后到终点的耗时之间取最小值

代码

#include<iostream>
#include<algorithm>
#include<cstring>
#include<cmath>
#include<queue>
//#include<map>
#include<vector>
//#include<set>
//#include<deque>
//#include<bitset>
//#include<functional>

#define ll long long
#define inf 0x3f3f3f3f
using namespace std;

const int N=2e6+10;
typedef pair<int,int>PII;
struct city{
	int val,id;
	bool operator <(const city &t)const
	{
		return val<t.val; 
	}
}speed[N];

vector<PII>e[N];
  
ll ans[N];//从i点开始,到终点的最小耗时
int dis[N];//x到i点的最短距离
int n,m;
	
void spfa(int x,ll v)
{
	vector<ll> dis(n+1,1e9);
	queue<int>q;
	q.push(x);
	dis[x]=0;
	while(!q.empty())
	{
		int t=q.front();
		q.pop();
		for(int i=0;i<e[t].size();i++)
		{
			int nxt=e[t][i].first;
			int nowd=e[t][i].second;
			if(nowd+dis[t]<dis[nxt])
			{
				dis[nxt]=dis[t]+nowd;
				q.push(nxt);
			}
		}
	}
	
	ans[x]=dis[n]*v;//从x点到终点的耗时
	//第二种情况为先骑到i点后换车,再骑到终点
	for(int i=1;i<n;i++)//枚举换车城市,取最小耗时
	{
		if(i!=x && ans[i]!=-1) ans[x]=min(ans[x],ans[i]+dis[i]*v);
	}
}	
	
int main()
{
	int t;cin>>t;
	while(t--)
	{
		cin>>n>>m;
		for(int i=1;i<=n;i++)
		{
			e[i].clear();
			ans[i]=-1;
		}
		
		for(int i=1;i<=m;i++)
		{
			int a,b,c;
			cin>>a>>b>>c;
			e[a].push_back({b,c});
			e[b].push_back({a,c});
		}
		for(int i=1;i<=n;i++) cin>>speed[i].val,speed[i].id=i;
		
		sort(speed+1,speed+1+n);//按减速系数升序排,速度快的优先
		
		for(int i=1;i<=n;i++) spfa(speed[i].id,speed[i].val);//每个点到终点的最小耗时
		cout<<ans[1]<<endl;
	}
	
	
	
	return 0;
}

  • 29
    点赞
  • 21
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值