Codeforces Round #816 (Div. 2)

A. Crossmarket

题目链接:Problem - A - Codeforces

样例输入: 

7
7 5
5 7
1 1
100000 100000
57 228
1 5
5 1

样例输出:

15
15
0
299998
340
5
5

题意:给定一个n*m的小方格,有两个人,一个人位于左上角,要去右下角,另一个人位于右下角要去左上角,一个人每走一步会消耗1个能量,但是这个人走过的地方会留下轨迹,如果另一个人走到了这个人走过的轨迹上,则可以选择耗费1能量使得这个人传送到另一个人已经走过的的任意地方,问两个人最少耗费的能量是多少?

分析:最简单的方式就是一个人先经过另一个人走到终点,然后另一个人再通过已经走过的路径传送一条边然后再走向终点,这样的花费一般来说是最小的,也有那种不传送的情况,比如起点终点重合就不需要传送,这个简单分析一下就好,下面是代码:

#include<iostream>
#include<algorithm>
#include<cstring>
#include<cstdio>
#include<cmath>
#include<vector>
#include<queue>
using namespace std;
const int N=1e5+10;
int main()
{
	int T;
	cin>>T;
	while(T--)
	{
		int n,m;
		scanf("%d%d",&n,&m);
		printf("%d\n",n+m-2+min(1,max(n-1,m-1))+min(n-1,m-1));
	}
	return 0;
} 

B. Beautiful Array

题目链接:Problem - B - Codeforces

 样例输入:

8
1 6 3 100
3 6 3 12
3 6 3 19
5 4 7 38
5 4 7 80
99978 1000000000 100000000 1000000000000000000
1 1 0 0
4 1000000000 1000000000 1000000000000000000

样例输出:

-1
-1
0 0 19
0 3 3 3 29
-1
-1
0
0 0 0 1000000000000000000

题意:给定一个长度为n的数组a,定义数组a的值就是,现在告诉我们数组的长度,以及k和数组a的值还有数组a中所有的元素和s,让我们构造一个满足上述定义的数组a。不能构造则输出-1.

分析:先来看一下什么情况是构造不出来的情况,首先如果要是s<k*b那么肯定是构造不出来的,还有一种情况是s>k*(b+1)-1+(k-1)*(n-1),其中k*(b+1)-1是第一个元素的值,而后面n-1个元素都放k-1,这样数组的值已经是b了,如果s比这还大,那么数组的值至少就是b+1。除了这两种情况之外都是有解的,我们可以比较一下k*(b+1)-1和s的大小,如果s<=k*(b+1)-1,那么我们直接让第一个元素等于s,其他元素为0即可,否则就让第一个元素为k*(b+1)-1,其他元素尽可能地放k-1,最后剩下的不到k-1的全放完,后面的直接全放0即可。

细节见代码:

#include<iostream>
#include<algorithm>
#include<cstring>
#include<cstdio>
#include<cmath>
#include<vector>
#include<queue>
using namespace std;
const int N=1e5+10;
int main()
{
	int T;
	cin>>T;
	while(T--)
	{
		long long n,k,b,s;
		scanf("%lld%lld%lld%lld",&n,&k,&b,&s);
		if(s<k*b) puts("-1");
		else if(s>(k*(b+1)-1+(k-1)*(n-1))) puts("-1");
		else
		{
			long long t=min(s,k*(b+1)-1);
			printf("%lld ",t);
			s-=t;
			for(int i=2;i<=n;i++)
				if(s>=k-1) printf("%lld ",k-1),s-=k-1;
				else if(s) printf("%lld ",s),s=0;
				else printf("0 ");
			puts("");
		}
	}
	return 0;
}
    

C. Monoblock

题目链接:Problem - C - Codeforces

样例输入: 

5 5
1 2 3 4 5
3 2
4 2
3 1
2 1
2 2

样例输出:

29
23
35
25
35

题意:给定一个长度为n的数组,我们定义一个区间的值为相等连续段的个数,求这个数组所有区间的值的和,此外我们会对这个数组进行m次操作,每次操作将一个元素值修改为另一个元素值,每次修改后都需要输出这个数组所有区间的值的加和。

分析:还是考虑每个数的贡献,为了防止重复计数,我们把一个区间内连续一段数所产生的贡献1看作是这段数中最左边的那个数产生的,比如3334433,这个区间值为3,其中1个是由最左边的那个3贡献的,另一个是最左边的4贡献的,还有一个是最右边一段连续的3中最左边的那个3贡献的,明白了这些定义之后我们来看一下如何计算一个数在整个数组中的贡献。

以1 2 3 3 4 5,我们来看一下这两个3的贡献,首先来看一下第一个3的贡献,这个3所贡献的区间的左边界可以是1,2,3,右边界可以是3,4,5,6,所以总的贡献区间数就是3*4=12,下面我们来看一下第二个3的贡献,第二个3的贡献区间的左边界可以取哪些值呢?可以取3么,不可以,因为这个区间中连续3的贡献是第一个3的,所以他的左边界只能是4,而右边界可以是任何值,也就是1*3=3,这样我们就能不重不漏地计算所有数的贡献。但是需要注意的一点是我们每次修改一个数后对他旁边的两个数的贡献也会产生影响,所以我们每次需要考虑这个数及其相邻的两个数

细节见代码:

#include<iostream>
#include<algorithm>
#include<cstring>
#include<cstdio>
#include<cmath>
#include<vector>
#include<queue>
using namespace std;
const int N=1e5+10;
int a[N],n;
long long cal(int pos)
{
	if(pos<1||pos>n) return 0;
	int l;
	if(pos==1)	l=1;
	else
	{
		if(a[pos]==a[pos-1]) l=1;
		else l=pos;
	}
	return 1ll*l*(n-pos+1);
}
int main()
{
	int m;
	cin>>n>>m;
	long long ans=0;
	for(int i=1;i<=n;i++) scanf("%d",&a[i]);
	for(int i=1;i<=n;i++)
		ans+=cal(i);
	while(m--)
	{
		int pos,val;
		scanf("%d%d",&pos,&val);
		ans-=cal(pos-1);
		ans-=cal(pos);
		ans-=cal(pos+1);
		a[pos]=val;
		ans+=cal(pos-1);
		ans+=cal(pos);
		ans+=cal(pos+1);
		printf("%lld\n",ans);
	}
	return 0;
}

D. 2+ doors

题意链接:Problem - D - Codeforces

 样例输入:

4 3
1 2 3
1 3 2
4 1 2

样例输出:

0 3 2 2 

题意:n个数,m个限制,每次限制给定i,j,x代表a[i]&a[j]=x,求最小的满足所有限制的字典序。

分析:我们按照位进行分析,位跟位之间是不会产生影响的,我们以某一位来进行分析。

如果两个数与值在第i位为0,那么在两者之间就连一条权值为0的边,否则连一条权值为1的边,我们首先处理权值为0的边,权值为0的边代表这一位上两个数均为0,然后我们就已经确定了这一位上一些数的取值,然后我们就遍历一边权值为1的边,如果某一个数已经确定是0,那么另一个数一定是1,因为两者至少有一个是1,最后我们整体处理那些权值为1的边,这些边按照字典序进行排序,先处理字典序小的,如果当前位还没确定就优先取0,然后把与该点有关的点全部取1,如果当前位已经确定,说明当前位一定是1,因为我们不可能确定一个数一定取0只能确定一个数一定取1,如果当前位取1,那么与当前点相关的点的取值我们可以不作限制,可以取0也可以取1.就是按照这样的贪心策略进行构造。细节见代码:

#include<iostream>
#include<algorithm>
#include<cstring>
#include<cstdio>
#include<cmath>
#include<vector>
#include<queue>
using namespace std;
const int N=1e6+10;
int a[N];
int u[N],v[N],z[N];
bool vis[N];
vector<pair<int,int> >p;
int main()
{
	int n,m;
	cin>>n>>m;
	for(int i=1;i<=m;i++)
		scanf("%d%d%d",&u[i],&v[i],&z[i]);
	for(int i=0;i<30;i++)
	{
		p.clear();
		for(int j=1;j<=n;j++) vis[j]=false;
		for(int j=1;j<=m;j++)
		{
			if(u[j]>v[j]) swap(u[j],v[j]);
			if(z[j]>>i&1)
				p.push_back({u[j],v[j]});
			else
				vis[u[j]]=vis[v[j]]=true;
		}
		sort(p.begin(),p.end());
		for(int j=0;j<p.size();j++)
		{
			int x=p[j].first,y=p[j].second;
			if(vis[x]&&((a[x]>>i&1)==0)&&(!vis[y])) vis[y]=true,a[y]+=1<<i;
			if(vis[y]&&((a[y]>>i&1)==0)&&(!vis[x])) vis[x]=true,a[x]+=1<<i;
		}
		for(int j=0;j<p.size();j++)
		{
			int x=p[j].first,y=p[j].second;
			if(vis[x]&&(!vis[y]))//当前这个数已经填了 
			{
				if(a[x]>>i&1)//当前这个数填的是1
					continue;//后面这个数可以随便填
				else
					a[y]+=1<<i,vis[y]=true;
			}
			else if(vis[y]&&(!vis[x]))//字典序大的数已经确定,那么这个数必然填的是1 
				continue; 
			else if(!vis[x])//当前这个数还没有填
				a[y]+=1<<i,vis[y]=true;
		}
	}
	for(int i=1;i<=n;i++)
		printf("%d ",a[i]);
	return 0;
}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值