ICode9

精准搜索请尝试: 精确搜索
首页 > 其他分享> 文章详细

Codeforces Round #742 (Div. 2)

2021-09-14 20:58:25  阅读:203  来源: 互联网

标签:742 int sum rmax Codeforces lmax Div id 1000


MEXor Mixup

m e x mex mex意思就是不在集合里面最小的正整数.所以给出一个 m e x mex mex那么他前面的所有的东西都是要选上的,然后前面的这些所有的异或之和(假设为 x x x)就可以预处理然后用 O 1 O1 O1的计算出来,因为给出的必须异或和为 y y y,所以我们还差一个 z = x   x o r   y z=x\ xor \ y z=x xor y,也就是 z   x o r   x = y z \ xor \ x=y z xor x=y,然后就是分情况了:

  • z = m e x z=mex z=mex就需要额外的两个数才行,因为不能取 m e x mex mex这个值嘛,不然 m e x mex mex就会变
  • z > m e x z>mex z>mex或者 0 < z < m e x 0<z<mex 0<z<mex,注意元素是可以重复的,所以就再要一个 z z z就行
  • z = 0 z=0 z=0,就不需要额外的了
#include<cstdio>
#include<algorithm>
#include<cstring>
#define maxn 400000
using namespace std;
int T,a[maxn],x,y;
signed main(){
	scanf("%d",&T);
	for(int i=1;i<=300000;i++) a[i]=a[i-1]^i;
	while(T--){
		scanf("%d %d",&x,&y);
		if(a[x-1]==y) {printf("%d\n",x);continue;}
		int temp=a[x-1]^y;
		if(temp>x) printf("%d\n",x+1);
		else if(temp==x) printf("%d\n",x+2);
		else printf("%d\n",x+1);
	}
	return 0;
}

Carrying Conundrum

这个题目的进位方式有一点奇怪,他是进到下下位去的.

  • 一开始的想法是:用 d p dp dp来转移,然后 d p [ i ] [ 1 / 0 ] dp[i][1/0] dp[i][1/0]表示第 i i i位是否进位得到的答案,然后转移就是很麻烦,和这一位,下下位都有关系,唯独下一位和这个没什么关系,所以就感觉像是有什么该转移的没有转移.
  • 于是我们就发现把单双数位单独的列出来就行了,这样子看的话就是正常的进位,也就是说我们如果要让和等于 15005 15005 15005的话,就拆成 105 105 105和 50 50 50就好了,然后答案就是 106 ∗ 51 − 2 106*51-2 106∗51−2.
  • 因为这种拆法使得进位变成正常的十进制,所以有:

106 = 0 + 106        = 1 + 105 . . . . . . . . . . . . .        = 106 + 0 106=0+106\\ \ \ \ \ \ \ =1+105\\.............\\ \ \ \ \ \ \ =106+0 106=0+106      =1+105.............      =106+0

  • 所以一共有 107 107 107种,同理 51 51 51就一共有 52 52 52种,所以乘法原理就是 106 ∗ 51 106*51 106∗51,只不过还要减去 2 2 2,就是因为不能 0 + 15005 = 15005 0+15005=15005 0+15005=15005这种情况.
#include<cstdio>
#include<algorithm>
#include<cstring>
#define maxn 50
#define int long long
using namespace std;
int T,n,dp[maxn],a[maxn];
signed main(){
	scanf("%lld",&T);
	while(T--){
		scanf("%lld",&n);
		int k=0; memset(dp,0,sizeof dp);
		while(n){
			a[++k]=n%10;
			n/=10;
		}
		int temp1=0,temp2=0;
		for(int i=k;i;i--){
			if(i%2) temp1=temp1*10+a[i];
			else temp2=temp2*10+a[i];
		}
		if(temp1==0 || temp2==0) printf("%lld\n",temp1+temp2-1);
		else{
			printf("%lld\n",(temp2+1)*(temp1+1)-2);
		}
	}
	return 0;
}

Expression Evaluation Error

  • 因为是改了之后是十一进制,所以高位尽量保持不变,用低位来保持加和的一样.
  • 然后 5000 5000 5000分成个 5 5 5个的话,虽然要让高位保持不变,但是 500 0 11 = 100 0 11 ∗ 5 5000_{11}=1000_{11}*5 500011​=100011​∗5,这样来看的话因为要拆成很多个,所以在保持最大的情况下每一个拆得越小越好,才让后面有东西可以拆,像上面那样拆就很好,虽然 500 0 11 = 200 0 11 + 300 0 11 5000_{11}=2000_{11}+3000_{11} 500011​=200011​+300011​也行,但是明显少了三个可以拆出来的.
  • 假设 5000 5000 5000分成 6 6 6个的话,先分成 1000 , 1000 , 1000 , 1000 1000,1000,1000,1000 1000,1000,1000,1000,现在剩下 1000 1000 1000,但是要分成两个,所以将 1000 1000 1000再先分成 100 100 100,最后只剩下一个了,那就是 5000 5000 5000减去所有之前的,也就是 900 900 900.至于为什么知道 1000 1000 1000分成两个时候是 100 100 100,这个就是和向下取整有点类似的操作,见代码
#include<cstdio>
#include<algorithm>
#include<cstring>
#include<cmath>
using namespace std;
int T,n,m;
signed main(){
	scanf("%d",&T);
	while(T--){
		scanf("%d %d",&n,&m);
		for(int i=1;i<m;i++){
			double temp=pow(10,(int)log10(n-(m-i)));
			printf("%.0f ",temp); n-=temp;
		}
		printf("%d\n",n);
	}
	return 0;
}

Non-Decreasing Dilemma

单点修改,区间查询,比较显然是要用数据结构的,这里要用到线段树合并,以前如果没有做过类似的题目的话是比较难想到的.

  • 先假设区间 [ a , b ] [a,b] [a,b]为 l l l,区间 [ b + 1 , c ] [b+1,c] [b+1,c]为 r r r,并假设现在已经处理好了 l , r l,r l,r这两个区间了,所以只用考虑如何合并这两个区间,更新到 [ a , c ] [a,c] [a,c],因为在叶子节点的时候,只有一个数的时候是已知答案的,然后慢慢向上更新,所以假设只考虑怎么合并更新
  • 设置几个变量: l m a x lmax lmax表示从左边第一个数字开始有几个数字是连续上升的, r m a x rmax rmax表示从右边开始最多有几个向左的是连续下降的,如 [ a , b ] = [ 1 , 2 , 3 , 5 , 7 , 1 , 5 , 2 , 3 , 4 ] [a,b]=[1,2,3,5,7,1,5,2,3,4] [a,b]=[1,2,3,5,7,1,5,2,3,4]是 l m a x = 5 , r m a x = 3 lmax=5,rmax=3 lmax=5,rmax=3. l v a l lval lval表示区间最左边那个数字是多少, r v a l rval rval表示最右边那个数是多少,还有一个 s u m sum sum也就是区间的所求答案是多少,还有 l e n len len,字面意思.
u.lval=l.lval; u.rval=r.rval;
u.lmax=l.lmax; u.rmax=r.rmax;
u.len=l.len+r.len; u.sum=l.sum+r.sum;
  • 上面这段代码我认为还是挺显然的,毕竟是合并嘛
  • 假设 l = [ 1 , 2 , 3 , 4 ] l=[1,2,3,4] l=[1,2,3,4],然后 r = [ 1 , 2 , 3 , 4 ] r=[1,2,3,4] r=[1,2,3,4]的话,就是上面那点点代码就行了,但是如果 r = [ 5 , 6 , 7 ] r=[5,6,7] r=[5,6,7]的话,就出现问题了,这两个合起来的 s u m sum sum不等于两个的直接相加,因为中间是可以连起来的比如可以有 [ 4 , 5 ] [4,5] [4,5],现在的 s u m sum sum还得再加上 l . r m a x ∗ r . l m a x l.rmax*r.lmax l.rmax∗r.lmax,也就是乘法原理,左边的选一个,再在右边选一个,跨过来,这样的话答案就统计完全了.
  • 还有 l m a x lmax lmax和 r m a x rmax rmax也得更新,像上面的那个区间情况就是 l . l e n = l . m a x l.len=l.max l.len=l.max的情况,也就是整个区间都递增,所以 r r r的左边的数如果是递增的话, u u u的 l m a x lmax lmax就会边长,就是 l . l e n + r . l m a x l.len+r.lmax l.len+r.lmax,然后 r m a x rmax rmax同理

其他的就简单了,注意 q u e r y query query得用 n o d e node node形式,因为答案要用 m e r g e merge merge得到的答案,而不是 l . s u m + r . s u m l.sum+r.sum l.sum+r.sum.

#include<cstdio>
#include<algorithm>
#include<cstring>
#define maxn 200010
#define int long long
using namespace std;
int n,q,a[maxn];
struct node{
	int sum,lmax,rmax,len,lval,rval;
}tree[maxn<<2];
void modify(node &x,int temp){
	x.len=x.sum=x.lmax=x.rmax=1;
	x.lval=x.rval=temp;
}
node merge(node l,node r){
	node u;
	u.lval=l.lval; u.rval=r.rval;
	u.lmax=l.lmax; u.rmax=r.rmax;
	u.len=l.len+r.len; u.sum=l.sum+r.sum;
	if(l.rval<=r.lval){
		if(l.lmax==l.len) u.lmax+=r.lmax;
		if(r.rmax==r.len) u.rmax+=l.rmax;
		u.sum+=l.rmax*r.lmax;
	}
	return u;
}
void build(int id,int l,int r){
	if(l==r) {modify(tree[id],a[l]); return ;}
	build(id*2,l,l+r>>1),build(id*2+1,(l+r>>1)+1,r);
	tree[id]=merge(tree[id*2],tree[id*2+1]);
}
void update(int id,int l,int r,int x,int val){
	if(l==r) {modify(tree[id],val); return ;}
	int mid=l+r>>1;
	if(mid>=x) update(id*2,l,mid,x,val);
	else update(id*2+1,mid+1,r,x,val);
	tree[id]=merge(tree[id*2],tree[id*2+1]);
}
node query(int id,int l,int r,int ll,int rr){
	if(l==ll && r==rr) return tree[id];
	int mid=l+r>>1;
	if(mid>=rr) return query(id*2,l,mid,ll,rr);
	else if(ll>mid) return query(id*2+1,mid+1,r,ll,rr);
	else return merge(query(id*2,l,mid,ll,mid),query(id*2+1,mid+1,r,mid+1,rr));
}
signed main(){
	scanf("%lld %lld",&n,&q);
	for(int i=1;i<=n;i++) scanf("%lld",&a[i]);
	build(1,1,n);
	for(int i=1,opt,l,r;i<=q;i++){
		scanf("%lld %lld %lld",&opt,&l,&r);
		if(opt==1) update(1,1,n,l,r);
		if(opt==2) printf("%lld\n",query(1,1,n,l,r).sum);
	}
	return 0;
}

题解发的很晚,见谅,快开学了,更新的比较慢,但是我会努力的.

标签:742,int,sum,rmax,Codeforces,lmax,Div,id,1000
来源: https://blog.csdn.net/qq_44590230/article/details/120296445

本站声明: 1. iCode9 技术分享网(下文简称本站)提供的所有内容,仅供技术学习、探讨和分享;
2. 关于本站的所有留言、评论、转载及引用,纯属内容发起人的个人观点,与本站观点和立场无关;
3. 关于本站的所有言论和文字,纯属内容发起人的个人观点,与本站观点和立场无关;
4. 本站文章均是网友提供,不完全保证技术分享内容的完整性、准确性、时效性、风险性和版权归属;如您发现该文章侵犯了您的权益,可联系我们第一时间进行删除;
5. 本站为非盈利性的个人网站,所有内容不会用来进行牟利,也不会利用任何形式的广告来间接获益,纯粹是为了广大技术爱好者提供技术内容和技术思想的分享性交流网站。

专注分享技术,共同学习,共同进步。侵权联系[81616952@qq.com]

Copyright (C)ICode9.com, All Rights Reserved.

ICode9版权所有