当前位置: 首页 > news >正文

Codeforces Round #816 (Div. 2)补题(A-E)

A. Crossmarket

题目链接:Problem - A - Codeforces

 题意:小红和小蓝走网格,小红从左下角走到右上角,小蓝从左上角走到右下角,小红经过的网格会产生传送门,小蓝可以通过传送门一次行动次数在小红走过的路上任意传送,走一格也需要花一次行动次数,求最少行动次数。

思路:让小红先走完(先向上走,在向右走),此时固定花费n+m-2次行动次数,此时小蓝脚上有传送门,可以传送到同列最底部或者同行最右部,因此肯定传送max(n-1,m-1)的路程最赚(此时花费1行动次数),然后走min(n-1,m-1)次数。

结论:当n=m=1时,输出0,否则输出m+n-2+min(n-1,m-1)+1

代码实现:

#include <bits/stdc++.h>
#define ll long long
using namespace std;
signed main()
{
       ios::sync_with_stdio(0);
       cin.tie(0);
       cout.tie(0);
       int t;
	   cin>>t;
	   while(t--)
	   {
	   	int n,m;
	   	cin>>n>>m;
	   	if(n==1&&m==1)
	   	cout<<0<<'\n';
	   	else
	   	cout<<n+m-2+min(m,n)<<'\n';
		} 
}

B. Beautiful Array

题目链接:Problem - B - Codeforces

题意:给n,k,b,s,要寻找一个长度为n的数组,每个元素除以k向下取整的和等于b,数组元素的和等于s,如果没有就输出-1。

思路:给定的b,初步假设有b个k,分配到数组的各位置中,若此时数组和小于s,就对数组中元素增加大小,但每个元素增加的量不能超过k-1,因为超过的话k的数量就大于b了,因此一个数组增加的量最大为(k-1)*n,可以将b*ki的值全部放在一个位置,然后所有位置增加大小,将数组和变为s。

代码实现:

#include <bits/stdc++.h>
#define ll long long
using namespace std;
const int maxn=1e5+10;
ll ans[maxn];
ll vis[maxn];
signed main()
{
        ios::sync_with_stdio(0);
        cin.tie(0);
        cout.tie(0);
        int t;
	    cin>>t;
	   	while(t--)
		{	
	   	ll n,k,b,s;
	   	cin>>n>>k>>b>>s;
	   	for(int i=1;i<=n;i++)
	   	ans[i]=0;
	   	ans[n]=b*k;//全放一起 
	   	ll temp=s-b*k;//此时数组和与s的差距 
		if(temp<0)
		{
			cout<<-1<<'\n';
			continue;
		 } 
	   		for(int i=1;i<=n&&temp;i++)
	   		{
	   			if(temp<=k-1)
	   			{
	   				ans[i]+=temp;
	   				temp=0;
				}
	   			else
			   {
					ans[i]+=k-1;
					temp-=k-1;	   	
			   }	
			}
		if(temp)//如果差距依旧存在,那么就找不到 
		cout<<-1<<'\n';
		else
		{
			for(int i=1;i<=n;i++)
			cout<<ans[i]<<' ';
			cout<<'\n';
		}
		} 
}

C. Monoblock

题目链接:Problem - C - Codeforces

题意:给一个数组,有一个函数g(l,r),返回数组[l,r]之间相邻相同元素组成的块的数量,进行m次操作,每次修改一个元素,并且要求输出此时对于所有(l,r)形成的g(l,r)的和。

思路:l,r的区间的个数总共为n*(n+1)/2,一个区间至少有一块,假设一开始数组元素都相同,那么所有区间g的和就为n*(n+1)/2,在此基础上,当出现两个相邻的元素不相同时,那么某些区间的g的值就会加1,考虑每个相邻的不同元素对产生贡献的区间的数量,假设i和i-1元素不同,那么产生贡献的区间的l可以在[1,i-1]中选择,r可以在[i,n]中选择,因此g的和增加(i-1)*(n-i+1),由此计算出未修改前的答案。

进行一次修改,考虑修改前后,该元素和相邻元素的关系,先撤销原来该元素与相邻元素对答案产生的贡献(当原来的元素和其相邻的元素不相同时),在结算修改后与相邻元素对答案的贡献(现在的元素与其相邻的元素不相同时)。

#include <bits/stdc++.h>
#define ll long long
using namespace std;
const int maxn=1e5+10;
ll a[maxn];
signed main()
{
        ios::sync_with_stdio(0);
        cin.tie(0);
        cout.tie(0);
        int n,m;
        cin>>n>>m;
        for(int i=1;i<=n;i++)
        cin>>a[i];
        ll ans=0;
        ans+=(ll)(n+1)*n/2;//每个区间的g至少大于1 
        for(int i=2;i<=n;i++)
        if(a[i]!=a[i-1])
        ans+=(ll)(i-1)*(n-i+1);//相邻两个不同元素产生的贡献 
        while(m--)
        {
        	int x,y;
        	cin>>x>>y;
        	if(x>1&&a[x]!=a[x-1])//撤销该元素与前一个元素的贡献 
        	ans-=(ll)(x-1)*(n-x+1);
        	if(x<n&&a[x]!=a[x+1])//撤销该元素与后一个元素的贡献 
        	ans-=(ll)x*(n-x);
        	a[x]=y;
        	if(x>1&&a[x]!=a[x-1])//结算该元素与前一个元素的贡献 
        	ans+=(ll)(x-1)*(n-x+1);
        	if(x<n&&a[x]!=a[x+1])//结算该元素与后一个元素的贡献
        	ans+=(ll)x*(n-x);
        	cout<<ans<<'\n';
		}
}

D. 2+ doors

主要思路来源:Codeforces Round #816 (Div. 2) A - D - 知乎 (zhihu.com)

(大佬写的太好了)

题目链接:Problem - D - Codeforces

题意:n个元素的数组,给q个按位或的等式,要求寻找一个满足条件且字典序最小的数组。

思路:对于a|b=c,考虑c二进制形式上每一位的数字,如果c在第i位的数字为0,那么a和b对应位置必定都为0,如果c在第i位元素为1,那么a与b之间至少有1个数字对应位置要有1,目前不考虑之后的情况,假设只放1个1,为了追求字典序最小,这个1肯定放在后面一个元素上,但是对于一个式子a|b=c,a和b可能都要放1,这考虑起来就非常的麻烦。

将a|b=c对应建一条连接a和b权值为c的无向边,记录其关系,存完图后,对于一个c,0对应的a与b的位置必定为0,而1的位置,先两个都放1,进行双重循环,外循环遍历二进制位数,内循环1-n从前往后遍历。假设当前元素为ai,对于当前位数j是否放1,若此时已经为0了,就跳过,如果此时为1,考虑图中和他相邻的点,并且边的权值的第j位是1(对于j位和ai有关系,两者之间至少要有一个1),这些点的第j位的数字都是1的话,那么ai的第j位的1就不需要了,变为0。

存在a|a=c的情况,此时a=c,不需要再将1改成0。

代码实现

#include <bits/stdc++.h>
#define ll long long
using namespace std;
const int maxn=1e5+10;
struct node{
	int to,v;
}; 
vector<node>vv[maxn];
inline void add(int u,int v,int w)
{
	vv[u].push_back({v,w});
	vv[v].push_back({u,w});
}
ll ans[maxn];
signed main()
{
        ios::sync_with_stdio(0);
        cin.tie(0);
        cout.tie(0);
        int n,m;
        cin>>n>>m;
        for(int i=1;i<=m;i++)
        {
        	int u,v,w;
        	cin>>u>>v>>w;
        	add(u,v,w);
		}
		for(int i=1;i<=n;i++)//初始都是1 
		ans[i]=(1<<30)-1;	
		for(int i=1;i<=n;i++)
		{
			for(int j=0;j<vv[i].size();j++)
			{
				int v=vv[i][j].v;
				ans[i]&=v;//a|b=c中,c的0的a和b对应位置也位0,而a和b其他位若不为0,那么就定为1 
			}
		}
		for(int j=30;j>=0;j--)
		{
			for(int i=1;i<=n;i++)
			{
				if((1<<j)&ans[i])
				{
					bool f=1;
					for(int k=0;k<vv[i].size();k++)
					{
						int to=vv[i][k].to;
						if(((1<<j)&ans[to])==0||to==i)//如果与之相连的点的对应位置为0或者有自己与自己按位或的式子(此时固定了) 
						{
							f=0;
							break;
						}
					}
					if(f)
					ans[i]-=(1<<j);//将j位的1变为0 
				}
			}
		}
		for(int i=1;i<=n;i++)
		cout<<ans[i]<<' ';
}

E. Long Way Home

题目链接:Problem - E - Codeforces

题意:n个城市,m条路,可以坐k次飞机,花费(u-v)*(u-v)时间,问从1到1-n所有城市的最短时间。

思路:首先考虑不坐飞机,那么就是简单的最短路问题,计算出每个点的dis后,考虑坐飞机。定义数组dp,用于记录先走一段路(坐飞机或者走路),最后坐飞机(可能不坐)到目的地,概念有点绕,主要是为了转移方程dp[u]=min(dp[u],dis[v]+(u-v)^2)(先到v,最后坐飞机(或者不坐)),然后再将dp的值传给dis,基于当前dis再进行最短路(飞机不一定只有最后坐,跑最短路寻求走路和坐飞机相间的最短时间),此时遇到一个问题,u和v要暴力遍历的话,一个点要遍历n次,那么时间复杂度就会达到n^2.

考虑斜率优化,首先推导公式:

temp1=dis[a]+(u-a)^2      temp2=dis[b]+(u-b)^2

我们要选择时间少的那个temp去和dp[u]作比较

假设temp1>temp2,那么

dis[a]+u^2-2ua+a^2>dis[b]+u^2-2ub+b^2

2u>((dis[b]+b^2)-(dis[a]+a^2))/(b-a)

右边式子形如k=(y2-y1)/(x2-x1)

对于一个点v,那么就将v定义成x,dis[v]+v^2定义成y。

将n个点都定义出对应的x和y画在坐标系上,横坐标x都是从1-n从小到大,对于两个点a,b:

当2u>k时(上面推的),b优于a,反之a优于b。但是此时u是(1-n)中的任意一个数,因此目前不能根据2u判断前后的优劣性。

对于三个点a,b,c形成的两个线段,考虑几种情况:

当前面的斜率k1>k2时,后期根据u结算时,若2u>k1,b优于a,那么2u>k2,此时c优于b,b点没有任何作用,若2u<k1时,a优于b,即使我不知道c与b的优劣性,b点依旧没有任何作用,所以可以直接删了。

当k1<k2时,若2u>k1,b优于a,不能确定2u>k2,无法确定b和c优劣性,若2u<k1,虽然说a优于b,但是不能删去b,因为u不能确定,b可能有时候会起作用。

因此利用先单调队列,建立斜率逐渐递增的坐标系,此时单调队列的head先不动,因为u不确定。

坐标系建完之后,遍历1-n所有的点,此时u是从小到大排序的,2u也是从小到大,因此对于一个u,可以直接根据其2u删除单调队列的首个元素(若开头两个点形成的斜率小于2u)。对于此时的u,最适合进行状态转移的点就为删除后的首个元素(斜率优化一般都这样)。

概要:求最短路->建立斜率递增的单调队列->根据从小到大的u删除队首元素->根据队首元素进行状态转移

按照以上步骤,进行k次循环

代码实现:

#include <bits/stdc++.h>
#define ll long long
#define int ll
#define double long double
using namespace std;
const int maxn=1e5+10;
const int inf=0x3f3f3f3f3f3f3f3f;
ll dp[maxn],dis[maxn];
bool vis[maxn];
int n,m,k;
struct edge{
	int to,v;
};
vector<edge>vv[maxn];
inline void add(int u,int v,int w)
{
	vv[u].push_back({v,w});
	vv[v].push_back({u,w});
}
struct node{
	int id,dis;
	friend bool operator<(const node&a,const node&b)
	{
		return a.dis>b.dis;
	}
};
void dij()//最短路板子 
{
	for(int i=1;i<=n;i++)
	vis[i]=0;
	dis[1]=0;
	priority_queue<node>q;
	for(int i=1;i<=n;i++)//唯一要注意的点,要把所有点放进队列,因为初始的dis不一定是inf 
	q.push({i,dis[i]});
	while(!q.empty())
	{
		node p=q.top();
		q.pop();
		int u=p.id;
		if(vis[u])
		continue;
		vis[u]=1;
		for(int i=0;i<vv[u].size();i++)
		{
			int to=vv[u][i].to;
			int w=vv[u][i].v;
			if(dis[to]>dis[u]+w)
			{
				dis[to]=dis[u]+w;
				q.push({to,dis[to]});
			}
		}
	}
}
//斜率优化部分 
double x[maxn],y[maxn];//存储x和y 
int que[maxn],head=1,tail=0;//单调队列 
inline double slope(int x1,int x2)//得到斜率 
{
	return (y[x2]-y[x1])/(x[x2]-x[x1]);
}
void solve()
{
	head=1;//初始化单调队列 
	tail=0;
	for(int i=1;i<=n;i++)
	{
		x[i]=i;
		y[i]=dis[i]+i*i;
		dp[i]=dis[i];
	}
	//head<tail是为了保留单调队列中的一个元素 
	for(int i=1;i<=n;i++)
	{
		while(head<tail&&slope(que[tail-1],que[tail])>slope(que[tail],i))//建立斜率单调递增的坐标系 
		tail--;
//		也可以这么写(可以画个图,两个互通的):
//		while(head<tail&&slope(que[tail-1],que[tail])>slope(que[tail-1],i)) 
//		tail--; 
//		que[++tail]=i;
	}
	for(int i=1;i<=n;i++)
	{
		while(head<tail&&slope(que[head],que[head+1])<2*i)//根据2i删掉队首元素 
		head++;
		int j=que[head];
		dp[i]=min(dp[i],dis[j]+(j-i)*(j-i));//根据队首元素状态转移 
	}
	for(int i=1;i<=n;i++)
	dis[i]=dp[i];//记录答案 
}
signed main()
{
        ios::sync_with_stdio(0);
        cin.tie(0);
        cout.tie(0);
        cin>>n>>m>>k;
        for(int i=1;i<=m;i++)
        {
        	int u,v,w;
        	cin>>u>>v>>w;
        	add(u,v,w);
		}
		for(int i=1;i<=n;i++)
		dis[i]=dp[i]=inf;
		dp[1]=0;
		dis[1]=0;
		dij();
		for(int i=1;i<=k;i++)
		{
			solve();
			dij();
		}
		for(int i=1;i<=n;i++)
		cout<<dis[i]<<' ';
}

相关文章:

  • 【牛客网-公司真题-前端入门篇】——百度2021校招Web前端研发工程师笔试卷(第二批)
  • 【Android应用与开发】DAY1-安装Android Studio报错整合及学习
  • Mybatis实战练习六【批量删除Mybatis参数传递】
  • 小白量化《穿云箭集群量化》(1)小白草根超级量化软件介绍
  • C语言指针操作(七)*指针数组和多重指针
  • 【python经验总结】我与bug的那些日子
  • <栈和队列及模拟实现>——《Data Structure in C Train》
  • 猿创征文|【Typescript】搭建TS的编译环境
  • 【项目管理】beautyeye
  • Connor学Android - HandlerThread和IntentService
  • Github每日精选(第31期):macOS 下的亮度和音量调节MonitorControl
  • Vue.js核心技术解析与uni-app跨平台实战开发学习笔记 第7章 Vue.js高级进阶 7.10 路由守卫
  • 金融核心系统云原生转型的三个挑战、六个误区和四个步骤
  • zsh安装以及ROS适配
  • 猿创征文|FlexManager与阿里云MQTT通讯
  • hexo+github搭建个人博客
  • 【剑指offer】让抽象问题具体化
  • 11111111
  • Docker下部署自己的LNMP工作环境
  • Invalidate和postInvalidate的区别
  • k8s 面向应用开发者的基础命令
  • LeetCode算法系列_0891_子序列宽度之和
  • Linux各目录及每个目录的详细介绍
  • PAT A1120
  • Python 反序列化安全问题(二)
  • REST架构的思考
  • spring security oauth2 password授权模式
  • Swoft 源码剖析 - 代码自动更新机制
  • 从setTimeout-setInterval看JS线程
  • 和 || 运算
  • 简单易用的leetcode开发测试工具(npm)
  • 码农张的Bug人生 - 初来乍到
  • 前端每日实战:70# 视频演示如何用纯 CSS 创作一只徘徊的果冻怪兽
  • 融云开发漫谈:你是否了解Go语言并发编程的第一要义?
  • 山寨一个 Promise
  • 移动端 h5开发相关内容总结(三)
  • 400多位云计算专家和开发者,加入了同一个组织 ...
  • AI又要和人类“对打”,Deepmind宣布《星战Ⅱ》即将开始 ...
  • 好程序员大数据教程Hadoop全分布安装(非HA)
  • ​ 无限可能性的探索:Amazon Lightsail轻量应用服务器引领数字化时代创新发展
  • # 日期待t_最值得等的SUV奥迪Q9:空间比MPV还大,或搭4.0T,香
  • #stm32驱动外设模块总结w5500模块
  • (2020)Java后端开发----(面试题和笔试题)
  • (ctrl.obj) : error LNK2038: 检测到“RuntimeLibrary”的不匹配项: 值“MDd_DynamicDebug”不匹配值“
  • (C语言版)链表(三)——实现双向链表创建、删除、插入、释放内存等简单操作...
  • (day 12)JavaScript学习笔记(数组3)
  • (vue)el-checkbox 实现展示区分 label 和 value(展示值与选中获取值需不同)
  • (附源码)计算机毕业设计SSM疫情居家隔离服务系统
  • (强烈推荐)移动端音视频从零到上手(上)
  • (十五)使用Nexus创建Maven私服
  • (未解决)jmeter报错之“请在微信客户端打开链接”
  • (五)关系数据库标准语言SQL
  • (一)UDP基本编程步骤
  • (转)Java socket中关闭IO流后,发生什么事?(以关闭输出流为例) .
  • ***检测工具之RKHunter AIDE